Using OpenID for authentication in Django

Tired of memorising login details for million different websites? Wanted to download or check somethning, but been prompted to created a user account on some random website?

We’ve all been in this situation. And for me personally, I would steer away from the site that forces me to register just to see some part of forum or similar. Mostly this is because I don’t want to provide my details and follow lengthy registration process.

I don’t mind to have an account (even though I forget about the website next day), but the whole process of typing in all the stuff, deciphering captcha images, logging in to my email account just to confirm registration, etc, etc. Why don’t they use OpenId? These days if you have Yahoo or Gmail account (and just who doesn’t?) you can use them to login to all websites that support OpenId.

In a nutshell, OpenId is a decentralised authentication standard. User (U) who has an OpenId account with a trusted company (C) wants to login to a website (W). W prompts with a login screen. U enters his/her OpenId URL (no personal information here provided at all). W then redirects to C login screen, where U is prompted to allow access to some parts of the information (usually it’s username only, sometimes email). If U is happy to provide this information, he then clicks login button and information is passed back to W. In this scenario, W will never get any personal information about U without explicit permission. And U benefits from having only one login that works for all websites.

Below is a simple diagram that shows how authentication is done when using OpenId to login to the websites:

Authentication with OpenID flow

Authentication with OpenID flow

Unfortunately Django does not have native support for OpenId, and their website recommends using django-authopenid library to perform authentication and login using OpenId.

Install required libraries

You need to install python-openid library first. Installation is a breeze, however there seems to be some issues with internal version number in the package, but let’s assume it’s all fine:

# easy_install python-openid
Searching for python-openid
Reading http://pypi.python.org/simple/python-openid/
Reading http://openidenabled.com/python-openid/
Reading http://www.openidenabled.com/openid/libraries/python/
Best match: python-openid 2.2.4
Downloading http://openidenabled.com/files/python-openid/packages/python-openid-2.2.4.zip
Processing python-openid-2.2.4.zip
Running python-openid-2.2.4/setup.py -q bdist_egg --dist-dir /tmp/easy_install-Xyxx61/python-openid-2.2.4/egg-dist-tmp-oexXVf
zip_safe flag not set; analyzing archive contents...
Adding python-openid 2.2.4 to easy-install.pth file
 
Installed /usr/lib/python2.5/site-packages/python_openid-2.2.4-py2.5.egg
Processing dependencies for python-openid
Finished processing dependencies for python-openid
# python
Python 2.5.1 (r251:54863, Jul 31 2008, 23:17:43)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> openid.__version__
'2.2.1'
>>> openid.__path__
['/usr/lib/python2.5/site-packages/python_openid-2.2.4-py2.5.egg/openid']
>>> openid.version_info
(2, 2, 1)
>>>

Then install django-authopenid. Installation seems to spit out some errors, but I guess these can be ignored as they don’t seem to cause any issues:

# easy_install django-authopenid
Searching for django-authopenid
Reading http://pypi.python.org/simple/django-authopenid/
Reading http://hg.e-engura.org/django-authopenid/
Reading http://code.google.com/p/django-authopenid/
Best match: django-authopenid 1.0.1
Downloading http://pypi.python.org/packages/source/d/django-authopenid/django-authopenid-1.0.1.tar.gz#md5=93d44b4ce40de55bed36c9ed292adb49
Processing django-authopenid-1.0.1.tar.gz
Running django-authopenid-1.0.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-UtVC7d/django-authopenid-1.0.1/egg-dist-tmp-oWjnYf
warning: no files found matching 'CHANGES.MD'
Sorry: TypeError: ('compile() expected string without null bytes',)
...
Adding django-authopenid 1.0.1 to easy-install.pth file
 
Installed /usr/lib/python2.5/site-packages/django_authopenid-1.0.1-py2.5.egg
Processing dependencies for django-authopenid
Finished processing dependencies for django-authopenid

Prepare Django application environment

There are several things you’d need to do:

  1. Create directory for application files
  2. I will use /var/app/application/ in this example

  3. Create directory for static content such as JS, CSS, HTML and images
  4. I will use /var/www/application/ in this example

  5. If you are using Apache web server add Virtual host definition and specify where web root is and when to pass control to mod_python module (note that all requests that do not start with /static/ will be passed to Django):
  6.     ServerName test.example.com
        DocumentRoot /var/www/application/
        ErrorLog /var/log/apache2/application-error.log
        CustomLog /var/log/apache2/application-access.log combined
        SetHandler mod_python
        PythonHandler django.core.handlers.modpython
        PythonPath sys.path+['/var/app/']
        SetEnv DJANGO_SETTINGS_MODULE application.settings
        SetEnv PYTHON_EGG_CACHE /tmp
        <Location "/static/">
            SetHandler None
        </Location>
  7. Also create database and configure appropriately in settings.py.
  8. Enable administration application for Django

Enable OpenID support

These were the basic steps to get your Django environment set-up, now let’s enable OpenID module:

  1. In settings.py add OpenID middleware, application and also tell Django where to find templates
  2. MIDDLEWARE_CLASSES = (
        ...
        'django_authopenid.middleware.OpenIDMiddleware',
    )
    INSTALLED_APPS = (
        ...
        'django_authopenid',
        'registration',
    )
    LOGIN_URL = "/account/signin/"
    TEMPLATE_DIRS = (
        ...
        os.path.join(os.path.dirname(__file__), 'templates')
    )
  3. In urls.py just include urls from django_authopenid package:
  4. urlpatterns = patterns('',
        ...
        (r'^account/', include('django_authopenid.urls')),
    )
  5. Add the following context processors list (majority is what’s loaded by default anyway, you just need to add one line, but since it’s a tuple, you cannot modify it, so you must redefine it):
    TEMPLATE_CONTEXT_PROCESSORS = (
       'django.core.context_processors.auth',
       'django.core.context_processors.debug',
       'django.core.context_processors.i18n',
       'django.core.context_processors.media',
       'django.core.context_processors.request',
       'django_authopenid.context_processors.authopenid',
    )
  6. Copy templates from the examples folder. Of course you’ll want them eventually to be fully integrated with your site, so that they match styles or even be incorporated in other pages, but for now, let’s just use the ones provided with the package. Location of the example templates obviously depends on your Linux distribution. On my Debian installation it was in /usr/lib/python2.5/site-packages/django_authopenid-1.0.1-py2.5.egg/example/templates. Copy examples/ directory to your Django application directory.

Test it

Once you’re done, test it. Navigate to http:///account/signin/ and if you followed this manual you should see something like this:

Django authopenid login screen

Django authopenid login screen

You may wonder what the login prompt on the left side is doing there. This is because Django authopenId logoc is to associate your OpenId account with local account and use standard Django “registration” middleware classes to handle all user management functions. If user does not exist – after you have selected your OpenID URL you will be asked to provide local username. You don’t need to memorise nor use it – it’s just to allow authopenId create association.

This may feel bit unintuitive, and I don’t particularly like this. I’m going to make some changes so that internal association is hidden from the end user and happens behind the scenes. This way my users are going to see only OpenId selector and login screen.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • LinkedIn
  • Live
  • Netvibes
  • NewsVine
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Yahoo! Bookmarks

Related posts:

  1. Python web crawler
  2. How to use generic views in Django
  3. Building and running Google Chrome OS on VirtualBox
  4. Query data from Django site with jQuery
  5. Building python 2.6.4 RPM for CentOS 5.4