repoze.who (Identification & Authentication for WSGI)
repoze.who is an identification and authentication framework for WSGI applications. It provides middleware to manage user identification, authentication, and authorization. The current stable version is 3.1.0, and it is actively maintained by the Pylons Project, though releases are infrequent.
Common errors
-
ImportError: cannot import name 'WhoMiddleware' from 'repoze.who.middleware'
cause Attempting to import `WhoMiddleware` directly from `repoze.who.middleware`, which was common in `repoze.who` 2.x but is not the standard or working method in 3.x.fixUse `from repoze.who.config import make_who_with_config` to configure and instantiate the `repoze.who` middleware. The `WhoMiddleware` class itself is an internal detail when using `make_who_with_config`. -
TypeError: 'NoneType' object is not callable (or similar error when authenticating)
cause Often occurs when an authenticator, identifier, or challenger is not correctly configured or fails to return the expected type/value. For example, if a plugin instance is `None` instead of a callable object.fixDouble-check your `who_config` dictionary to ensure all plugins (identifiers, authenticators, challengers) are correctly instantiated and passed. Verify that your custom authenticators return `None` on failure or the correct identifier on success. -
KeyError: 'repoze.who.identity' (or other repoze.who keys not found in environ)
cause The `repoze.who` middleware has not processed the request, or the `add_new_r_w_to_environ` function was not called, preventing the `repoze.who` object from being properly set up in the WSGI environment.fixEnsure that your WSGI application is correctly wrapped by the `repoze.who` middleware created via `make_who_with_config`. Also, verify that `add_new_r_w_to_environ` is invoked early in your request processing chain.
Warnings
- breaking repoze.who 3.x (and higher) is Python 3.9+ only. Earlier versions (2.x) supported Python 2.x. Porting from 2.x requires adapting to Python 3 syntax and potentially updated configuration approaches.
- breaking The primary method for instantiating the WhoMiddleware has changed. Direct import of `WhoMiddleware` from `repoze.who.middleware` (common in 2.x) is not the recommended approach in 3.x; instead, use `make_who_with_config`.
- gotcha Forgetting to call `add_new_r_w_to_environ` after creating the `repoze.who` middleware means that the `repoze.who` object (containing identifiers, authenticators, etc.) will not be available in the WSGI environment for subsequent requests, leading to unexpected behavior or errors.
- gotcha Configuration of `repoze.who` relies on a dictionary structure passed to `make_who_with_config`. Incorrectly formatted dictionaries (e.g., missing required keys, invalid plugin instances) will lead to runtime errors that can be hard to diagnose.
Install
-
pip install repoze.who
Imports
- make_who_with_config
from repoze.who.config import make_who_with_config
- WhoMiddleware
from repoze.who.middleware import WhoMiddleware
from repoze.who.config import make_who_with_config # Indirectly used
- add_new_r_w_to_environ
from repoze.who.config import add_new_r_w_to_environ
Quickstart
import os
from webob.dec import wsgify
from webob import Response
from repoze.who.config import make_who_with_config, add_new_r_w_to_environ
from repoze.who.plugins.basicauth import BasicAuthPlugin # Example plugin
# Dummy WSGI application
@wsgify
def my_app(request):
identity = request.environ.get('repoze.who.identity')
if identity:
username = identity.get('repoze.who.userid')
return Response(f'Hello, {username}! You are authenticated.')
else:
return Response('Please authenticate.', status=401)
# Configure repoze.who
# For real applications, use proper credential storage
users_db = {'admin': 'secret', 'user': 'password'}
def my_authenticator(environ, identity):
if 'login' in identity and 'password' in identity:
login = identity['login']
password = identity['password']
if users_db.get(login) == password:
identity['repoze.who.userid'] = login # Store userid
return login
return None
# Example challenge (basic auth)
basic_auth_plugin = BasicAuthPlugin('My Realm')
# Who config dictionary (simplified)
who_config = {
'identifiers': [('basic_auth_identifier', basic_auth_plugin)],
'authenticators': [('my_auth', my_authenticator)],
'challengers': [('basic_auth_challenger', basic_auth_plugin)],
'log_stream': None,
'log_level': 10 # DEBUG
}
# Create WhoMiddleware
who_middleware_app = make_who_with_config(my_app, who_config)
# Wrap the app to ensure 'repoze.who' object is in environ
@wsgify
def wrapped_app(request):
add_new_r_w_to_environ(request.environ, who_config)
return who_middleware_app(request)
# To run this with a WSGI server (e.g., waitress):
# pip install webob waitress
# from waitress import serve
# serve(wrapped_app, host='0.0.0.0', port=8000)
# (Uncomment above lines to run)