Django Authentication Module
============================
.. module:: openid2rp.django
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',
'.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.
The next step is to modify your existing login view. You need accept
an OpenID URI as alternative to username / password. There are
JavaScript support libraries available to help you with the URIs for
the different providers and their logos.
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 result of this
call is an ``HttpResponse`` object you must return as view result.
After authentication, the provider will send the users browser back to
your newly created second login view. Call Django's ``authenticate``
in there with one keyword arguments ``openidrequest`` for the request object.
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.
Functions
*********
The Django application can use the following API:
.. function:: openid2rp_django.auth.preAuthenticate(uri, answer_url, sreg = (('nickname', 'email'), ()), ax = ((openid2rp.AX.email, openid2rp.AX.first, openid2rp.AX.last), ()),reuse_session = True) -> response
``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.
``reuse_session`` is intended as possibility for the application to
work around broken OpenID providers. It disables the re-using of
OpenID sessions for the given claim. If your provider sends an
'openid.invalidate_handle' parameter in the returning request,
and the OpenID authentication ends with a session error,
try to set this to 'False'. Normally, you don't need to touch
this parameter.
The 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.
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.
.. function:: django.contrib.auth.authenticate(openidrequest=None) -> user
In the handling of the providers redirection back to your site, you
need to call Django's ``authenticate`` function with a keyword
parameter. ``request`` is the ``HttpRequest`` input object for your
view code. The authentication provider fetches all relevant
information from it.
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 input. In a later call
to ``linkOpenID``, use 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``.
.. function:: openid2rp_django.auth.getOpenIDs(user) -> ids
Returns a string list of stored OpenID claim URIs for this Django user
object. This is intended for your user settings view.
.. function:: openid2rp_django.auth.linkOpenID(user, claim) ->
Links the given Django user object to the given OpenID claim.
.. function:: openid2rp_django.auth.unlinkOpenID(user, 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``.