Django Authentication Module
This is the implementation of a Django authentication backend for OpenID, based on the openid2rp package. It is automatically installed together with openid2rp.
In contrast to most (all ?) other Django OpenID authentication packages, this one does not try to cover any view aspects. All error cases are reported by exceptions, which you can render in whatever way you prefer.
Using the authentication backend
It is assumed that you have a working Django app with standard login functionality.
Add openid2rp.django to INSTALLED_APPS. Example:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', 'openid2rp.django', '<yourapp>.front' )
Add openid2rp.django.auth.Backend to AUTHENTICATION_BACKENDS.Example:
AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'openid2rp.django.auth.Backend' )
Do once a python manage.py syncdb to create the relevant tables.
Add a second view, which will receive the answer from the OpenID authentication provider.
In your original login view code, call openid2rp_django.auth.preAuthenticate instead of Django's authenticate if you want to perform an OpenID authentication. Input parameters are the user-provided OpenID URI, and the (absolute !) URL of the newly created view. The results of this call are an HttpResponse object you must return as view result, and the normalized OpenID URI ('claim') you must store in the user session.
After authentication, the provider will send the users browser back to your newly created second login view. Call Django's authenticate in there with two keyword arguments for the request object and the claim stored in the session. The result is a Django User object (if a user record in the database has this claim attached), a Django AnonymousUser object (if the claim could not be found, but OpenID authentication was ok), or an Exception if anything went wrong. In the second case, you might offer some user registration facility. You can use openid2rp_django.auth.linkOpenID to assign claims to Django user objects, so that authenticate is successfull the next time.
The Django application can use the following API:
uri is the OpenID URI input from the user. answer_url is the absolute address of the view that will later call authenticate(). You can realize this view in whatever way you prefer, for example also by using the original login view with another GET parameter. sreg resp. ax allow you to request a set of information attributes from the authentication provider. Check the openid2rp and OpenID documentation for details.
The first result is the HttpResponse object you should directly return from the view code after calling preAuthenticate. It contains a 307 redirection to the authentication provider URL, so that the user's browser goes forward to the actual provider authentication screen. The second result is the normalized version of the OpenID URI, called a 'claim'. You need this in the following call to authenticate(), so store it somewhere (e.g. in the user session).
If something goes wrong, one of the following errors is raised:
- IncorrectURIError: The provided URI couldn't be normalized.
- IncorrectClaimError: The OpenID discovery step for this claim failed. This might be a typo, but can also be reasoned by an unavailable provider.
In the handling of the providers redirection back to your site, you need to call Django's authenticate function with two keyword parameters. request is the HttpRequest input object for your view code. The authentication provider fetches all relevant information from it. The second parameter must be claim, which is one of the results from preAuthenticate we asked you to store somewhere.
The result of this call is either:
A Django User object. In this case, the OpenID claim was successfully authenticated, and the backend found a user in the database with this claim attached. The object as additional attributes:
- openid_claim: The claim that was finally authenticated. Depending on the OpenID provider, this might or might not be the original method input. In a later call to linkOpenID, use only this one.
- openid_ax: A dictionary of received AX values. The django.contrib.auth.AX dictionary contains a list of standardized key names. Check the openid2rp documentation for more information.
- openid_sreg: A dictionary of received SREG values.
A Django AnonymousUser object. In this case, the claim could not be related to any user in the database, but the OpenID authentication was ok. The object has the same additional attributes as above. In this situation, you should normally proceed with some new user registration functionality. You can use the AX / SREG data to pre-fill some registration form.
One of the following exceptions:
- MissingSessionError: There is no stored session for this result. This typically means that you forgot to start with preAuthenticate.
- AuthenticationError: Something went wrong in the OpenID authentication process. The exception message contains more information.
- IncompleteAnswerError: This is normally the providers fault.
- MultipleClaimUsageError: The authenticated claim was linked to multiple users, which is not valid. You need to correct your database.
- ReplayAttackError: The nonce checking mechanisms identified an answer that was already given before.
- TookTooLongError: The authentication at the provider side took too long. You can override the default value (5 min) in your settings file with the parameter OPENID2RP_MAXLOGINDELAY.
Returns a string list of stored OpenID claim URIs for this Django user object. This is intended for your user settings view.
Links the given Django user object to the given OpenID claim.
Unlinks the given Django user object from the given OpenID claim. This is intended for your user settings view.
Time, clocks, and the ordering of events
For the different timestamp checks, the authentication backend allows a maximum derivation of authentication provider clock and relaying party clock of 5 min. You can override this default value in your Django settings file with the parameter OPENID2RP_MAXTIMESHIFT.