I have my Flask app hosted in IIS in our intranet. In Flask, I'm able to get the www-authenticate header, but I need to determine the windows username. I did have Basic Authentication enabled and was able to parse out the username via that method, but I want this to be transparent to the user. In IE I have the option set to auto login to intranet sites so they're not prompted for a username and password.
I am able to get a string that can either begin with NTLM or Negotiate (depending on the setting in IIS) and a long auth string. What is a reliable way I can decode this in python/Flask?
Got it.
class RemoteUserMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
user = environ.pop('HTTP_X_PROXY_REMOTE_USER', None)
environ['REMOTE_USER'] = user
return self.app(environ, start_response)
app.wsgi_app = RemoteUserMiddleware(app.wsgi_app)
Then in the view by doing this:
username = str(request.environ.get('LOGON_USER'))
Related
Simply saying, I am developing an app using Flask. For this app I am trying to implement a Single Sign-On, so a user never needs to enter credentials, e.g. username and password.
The authentication and authorization in this case will go through Kerberos together with LDAPS. The Kerberos part is not done yet, however, Kerberos is intended to get a "username" via a middleware currently logged into a system (Windows), when requesting an app's link. Afterwards this variable i.e. "username" will be proceeded with LDAPS, to check whether a user belongs to Active Directory or not. If yes - provide access and permission to a web site, if no - forbid.
However, since my user wont type anything, I do not understand whether I need to use either the Flask Form (flask-wtf) or the Flask Login (flask-login e.g. UserMixin) as well as how shall I provide an access to my user?
I was able to set up the FlaskLDAP3Login in 'config.py' and than run the '__init__.py'
from flask import Flask
from config import Config
from flask_login import LoginManager
from flask_ldap3_login import LDAP3LoginManager
app = Flask(__name__)
app.config.from_object(Config)
login_manager = LoginManager(app) # Setup a Flask-Login Manager
ldap_manager = LDAP3LoginManager(app) # Setup a LDAP3 Login Manager
from app import routes
than I got the following exception:
Exception: Missing user_loader or request_loader. Refer to
http://flask-login.readthedocs.io/#how-it-works for more info.
than I found this answer, but using this decorator #login_manager.user_loader is probably not enough, is not it?
My assumption is to create a decorator, similar to this one that will allow/forbid an access to a user:
import getpass
from flask import Flask, request, make_response
from functools import wraps
def auth_required(f):
#wraps(f)
def decorated(*args, **kwargs):
current_user = getpass.getuser() # current_user will be later acquired via Kerberos
auth = ldap_manager.get_user_info_for_username(current_user)
if auth:
return f(*args, **kwargs)
return make_response('Could not verify your login!', 401, {'WWW-Authenticate': 'Basic realm="You are not our user!"'})
return decorated
Also, I cannot find a similar or even related thread e.g.
github/flask-ldap3-login/flask_ldap3_login/forms.py
Authenticate with Flask-LDAP3-Login based on group membership
Flask Authentication With LDAP
Integrate LDAP Authentication with Flask
The Flask Mega-Tutorial Part V: User Logins
I have two separate Flask applications, one is an API with the domain "admin.company.com" and the second one is a dashboard under the domain "dashboard.company.com".
My main goal is to secure the api and enable authentication.
I set up authentication on the api and when I'm testing it via the swagger-ui it works good with no issues. I manage to authenticate myself and submit requests. On top of that, in the token_required() below I coded a section that expects to receive JWT and decode it:
def token_required(f):
#wraps(f)
def decorator(*args, **kwargs):
token = None
if 'jwt-token' in request.headers:
token = request.headers['jwt-token']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
current_user = False
# for authentication via the swagger-ui
if token == 'my_awesome_password':
current_user = 'admin'
else:
data = jwt.decode(token, app.secret_key)
current_user = 'admin' if data['public_id'] == 'admin' else False
if not current_user:
return jsonify({'message': 'token is invalid'})
except:
return jsonify({'message': 'token is invalid'})
return f(*args, **kwargs)
return decorator
The problem is with the dashboard application:
On the dashboard app, I configured the /login route to generate JWT (which needs to be sent to the api app in every HTTP request I made), then do a set_cookie() with httpOnly=True flag (for security purposes), but then how can the JavaScript access it when it has to make XHR requests? Having the httpOnly flag on makes it unreachable.
If using httpOnly=True is not the ideal way in this case, how can I tackle it and make sure the JWT will always be sent to the api app in the request headers?
I'm using PyJWT package.
Thanks for any help.
If you use JWTs for XHR requests, then you don't necessarily need to use cookies – the browser/consumer could store the token and attach it to every request. If you do want to use cookies (this is also a viable option) then you don't need to worry about attaching the JWT as the browser will do that automatically for the domains you specified in the set_cookie command.
I have a simple bokeh server application and I want to expose it on a Linux-based Azure node. The server is there up and running.
My question is: how to protect the content by username and password? I do not need necessarily authentication of users.
My ideas so far (not tried, may not work)
To create an extra bokeh server page with a text field.
On the callback for a button, to add the test if the password fits. If it does, to redirect to the original server page. Otherwise, inform the user about wrong credentials.
You can try to disable generation of session id's by bokeh server and generate them by external application only after user authentication:
(Based on this part of bokeh documentation)
Generate secret key with bokeh secret command:
$ bokeh secret
oIWDL7DVYCaBJG9eYQ2Wvf2f2uhOAIM8xNS8Kds3eizV
Set BOKEH_SECRET_KEY environment variable to generated value;
$ export BOKEH_SECRET_KEY=oIWDL7DVYCaBJG9eYQ2Wvf2f2uhOAIM8xNS8Kds3eizV
Set another environment variable:
$ export BOKEH_SIGN_SESSIONS=True
Run bokeh server with --session-ids external-signed argument:
$ bokeh serve myApp --session-ids external-signed
In this mode user should provide valid (signed) session id to access bokeh server.
Run simple external process to ask users for login and password and generate id's for them.
Here is the example based on snippet from Flask documentation:
from functools import wraps
from flask import request, Response, redirect, Flask
from bokeh.util import session_id
app = Flask(__name__)
def check_auth(username, password):
return username == 'valid_user' and password == 'valid_password'
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
#app.route('/')
#requires_auth
def redirect_to_bokeh():
s_id = session_id.generate_session_id()
return redirect("http://<bokeh-server-addr>:<port>/?bokeh-session-id={}".format(s_id), code=302)
if __name__ == "__main__":
app.run()
Now to access bokeh server user should go to Flask application and specify login and password.
I want to enable basic access authentication in my Django project like this:
I found this post by Google, and changed my settings.py following the first answer:
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
...
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.RemoteUserBackend',
)
But the authentication window doesn't come out. The project is still in debug mode and I run it by python ./manage.py runserver.
I can think of multiple ways to do this. If you want your entire django application protected by basic authentication then you can add an authentication middleware to your wsgi app. Django creates a default wsgi application in your project. Add the following middleware to this wsgi.py file:
class AuthenticationMiddleware(object):
def __init__(self, app, username, password):
self.app = app
self.username = username
self.password = password
def __unauthorized(self, start_response):
start_response('401 Unauthorized', [
('Content-type', 'text/plain'),
('WWW-Authenticate', 'Basic realm="restricted"')
])
return ['You are unauthorized and forbidden to view this resource.']
def __call__(self, environ, start_response):
authorization = environ.get('HTTP_AUTHORIZATION', None)
if not authorization:
return self.__unauthorized(start_response)
(method, authentication) = authorization.split(' ', 1)
if 'basic' != method.lower():
return self.__unauthorized(start_response)
request_username, request_password = authentication.strip().decode('base64').split(':', 1)
if self.username == request_username and self.password == request_password:
return self.app(environ, start_response)
return self.__unauthorized(start_response)
Then, instead of calling
application = get_wsgi_application()
You should use:
application = AuthenticationMiddleware(application, "myusername", "mypassword")
This will ensure that every request to your django server goes through basic authentication.
Please note that unless you're using HTTPS then basic authentication isn't secure and the user credentials will not be encrypted.
If you only want some of your views to be covered by basic authentication then you can modify the above class to be a function decorator :
def basic_auth_required(func):
#wraps(func)
def _decorator(request, *args, **kwargs):
from django.contrib.auth import authenticate, login
if request.META.has_key('HTTP_AUTHORIZATION'):
authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
if authmeth.lower() == 'basic':
auth = auth.strip().decode('base64')
username, password = auth.split(':', 1)
if username=='myusername' and password == 'my password':
return func(request, *args, **kwargs)
else:
return HttpResponseForbidden('<h1>Forbidden</h1>')
res = HttpResponse()
res.status_code = 401
res['WWW-Authenticate'] = 'Basic'
return res
return _decorator
Then you can decorate your views with this to activate basic authentication.
Note that the username/password are both hardcoded in the examples above. You can replace that with your own mechanism.
Hope this helps
As mentioned in the docs, the REMOTE_USER is set by the web server. Typically you will need to configure a web server like Apache or IIS to protect a site or a directory using HTTP Basic Authentication.
For debug purposes, I suggest setting a dummy user in the manage.py, say:
import os
from django.conf import settings
if settings.DEBUG:
os.environ['REMOTE_USER'] = "terry"
So I'm working with AppEngine (Python) and what I want to do is to provide OpenID Login setting a Default provider so the user can Log-In without problems using that provider. The thing is, I want to prompt the user a Password right after they login in order to show static content (HTML Pages); If the user doesn't enter the correct password then I want to redirect them to another page. The protection has to be server side please :) Any Ideas??
P.S. I'm seeking for a solution similar to ".htaccess/htpasswd" but for app engine.
AFAIK, GAE does not support such setup (static password after OpenID login).
The only way I see to make this work would be to serve static content via your handler:
Client makes a request for static content
Your handler is registered to handle this URL
Handler checks is user is authenticated. If not, requests a password.
When authenticated, handler reads static file and sends it back to user.
Try this out, you can mimic the .htaccess style password with Google App Engine:
def basicAuth(func):
def callf(webappRequest, *args, **kwargs):
# Parse the header to extract a user/password combo.
# We're expecting something like "Basic XZxgZRTpbjpvcGVuIHYlc4FkZQ=="
auth_header = webappRequest.request.headers.get('Authorization')
if auth_header == None:
webappRequest.response.set_status(401, message="Authorization Required")
webappRequest.response.headers['WWW-Authenticate'] = 'Basic realm="Kalydo School"'
else:
# Isolate the encoded user/passwd and decode it
auth_parts = auth_header.split(' ')
user_pass_parts = base64.b64decode(auth_parts[1]).split(':')
user_arg = user_pass_parts[0]
pass_arg = user_pass_parts[1]
if user_arg != "admin" or pass_arg != "foobar":
webappRequest.response.set_status(401, message="Authorization Required")
webappRequest.response.headers['WWW-Authenticate'] = 'Basic realm="Secure Area"'
# Rendering a 401 Error page is a good way to go...
self.response.out.write(template.render('templates/error/401.html', {}))
else:
return func(webappRequest, *args, **kwargs)
return callf
class AuthTest(webapp.RequestHandler):
#basicAuth
def get(self):
....
How-To: Dynamic WWW-Authentication (.htaccess style) on Google App Engine