Improved WSGI Basic Authentication Middleware in Python

, a 8-minute piece by Dev Mukherjee Dev Mukherjee

While hunting around the Web for a Basic Authentication WSGI Middleware, I came across Edd Mann's blog post that outlined a nice, elegant solution.

Supplying the same value in the username and password fields authenticates a user and hands over control to the wrapped WSGI application. A perfect solution to use in development environments, where in production the Web server configuration would handle authentication.

It however does not set the REMOTE_USER environment variable with the logged in username. Determining the logged in user would be quite a common use case for applications.

I made a few quick enhancements so the middleware passes on the user information to the WSGI application and lets you set the authentication realm name.

class BasicAuthMiddleware(object):

    def __init__(self, app, realm="Login"):
        self._app = app
        self._realm = realm
        self._usernmae = None

    def __call__(self, environ, start_response):
        if self._authenticated(environ.get('HTTP_AUTHORIZATION')):
            environ['REMOTE_USER'] = self._username
            return self._app(environ, start_response)
        return self._login(environ, start_response)

    def _authenticated(self, header):
        from base64 import b64decode
        if not header:
            return False
        _, encoded = header.split(None, 1)
        decoded = b64decode(encoded).decode('UTF-8')
        username, password = decoded.split(':', 1)
        self._username = username
        return username == password

    def _login(self, environ, start_response):
        start_response('401 Authentication Required',
            [('Content-Type', 'text/html'),
             ('WWW-Authenticate', 'Basic realm="%s"' % self._realm)])
        return [b'%s' % self._realm]

a basic WSGI demo application that displays the logged in username (adapted from Edd's original example):

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b"Hello, %s!" % environ.get('REMOTE_USER')]


if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    httpd = make_server('', 8080, BasicAuthMiddleware(app))
    print('Serving on port 8080...')
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print('Goodbye!')

Next Up: a 1-minute piece by Dev Mukherjee Dev Mukherjee

The Power of Displacement

Read more