TL;DR: I tried out four different Django social authentication and registration packages. The only one that worked out of the box was django-allauth, though django-social-auth looks like it could be promising. django-allauth is the only one that supports username/password registration as well as social registration.
One of those silly little things that almost any modern web application needs is authentication. And with authentication comes usernames, passwords, perhaps email confirmations — the dreary routine. A lot of newer sites decide to offload the chore of authentication to third parties through OpenID, Facebook Connect, Google accounts, etc. This makes a lot of sense — but it’s onerous to code up your own connectors to all of these providers. It is the perfect job for a simple pluggable app that lets your web framework handle authentication.
The rails community has OmniAuth, a comprehensive, well used and well tested solution. But in the Django world, we have no less than four distinct authentication packages for this purpose, each of which has hundreds of Github followers, thousands of downloads, and relatively current commit histories:
(Django Packages grids I used to pick these: authentication and facebook-authentication)
I did not include the venerable and relatively canonical django-registration here because it only ships with username/password registration, and not social registration.
My requirements:
- Support the major 3rd party providers — facebook, twitter, google and OpenID.
- Support username/password registration as well.
- Low barrier to entry — it should work, more or less, out of the box.
- High customizability: registration becomes a super important part of the new user experience; so the details matter.
To test, I just created a simple app that would consist of nothing but registering, signing in and signing out. I tested each auth app with its own Django 1.3 project, with its dependencies installed in its own virtualenv. To start with, because it’s easier in a test scenario without a registered application, I’m focusing on OpenID. I’m using each package from it’s latest master branch.
Jump to review:
- django-socialregistration
- Django-Socialauth
- django-social-auth
- django-allauth
django-socialregistration
Installation
Documented basic steps:
- Settings:
- Add socialregistration to INSTALLED_APPS.
- Add django.core.context_processors.request to TEMPLATE_CONTEXT_PROCESSORS
- URLs: Add conf for socialregistration.urls, such as:
url(r'^social/', include('socialregistration.urls')),
- Templates: No template is provided for login/logout/register, only for the various stages in authenticating with social providers. However, templatetags are provided to easily add forms for the various providers — such as:
{% load openid_tags %}
{% openid_form %}
This needs to be put in a login form somewhere. Logout happens via the normal django auth mechanisms.
Undocumented steps:
- It relies on the Sites framework to get your site’s URL for callbacks from auth providers. Set the correct site URL in admin.
How it looks
With only an OpenID form and no extra design, it’s pretty plain:
Typing in an OpenID and clicking “Connect with OpenID” sends me to my OpenID provider, and then back to the application to set up Username and Email. So far so good. The template would clearly need to be made a little prettier, but that’s easy to override.
I try typing in a username, and an email address. Uh oh! I get an error.
And not a friendly error at all. “Enter a valid value”? What, pray tell, is valid? It looks like to use this I’ll have to override templates as well as the form class used for setup to put nicer help text in the error message. That means I’ll have to override the URL to the “setup” method in order to pass it a different UserForm class. Let’s try a username without spaces:
Oh no! 500 error. It looks like something’s broken with the OpenID connect code. I dug into this for a while trying to get it to work, but no luck. I filed a bug report here.
Outcome
500 error when trying to connect with OpenID. No dice.
Django-Socialauth
On to the first of the confusingly named Django Social Auth packages.
Installation
Well, the documentation is very thin — this is all they give us:
- Install required libraries.
- Get tokens and populate in localsettings.py.
- Set the token callback urls correctly at Twitter and Facebook.
- Set the authentication_backends to the providers you are using.
… ok. Not much help. But there is an example_project directory that gives us some hints.
Undocumented steps:
How it looks
Navigating to /socialauth/, we get an OpenID form — makes sense that this is the only one, because I didn’t set up any tokens for Facebook or Twitter.
But when I enter my OpenID and click “Sign-In”, I’m just redirected back to LOGIN_REDIRECT_URL, without being signed in — no indication as to why. I spent a while poking around trying to figure out what was going on, but no dice. Firebug logs requests going out to my OpenID provider and back, but the app seems to just not authenticate. I tried using /socialauth/openid/ as a URL to start the login from, but get a CSRF error.
Outcome
No errors, but the OpenID chain doesn’t result in me being authenticated. It doesn’t work.
django-social-auth
On to the second Django Social Auth package. This one shows more promise — it seems to have more current commits, there’s a solid effort underway to actually add tests (admitedly a difficult thing when dealing with so many external providers and API keys), and it seems designed in a nicely pluggable way for adding other providers in the future. On top of that, it actually has Sphinx docs!
Installation
Documented steps:
- Install dependencies. pip has us covered; it already got them when installing the package.
- Settings:
- Add social_auth to INSTALLED_APPS.
- Add the desired AUTHENTICATION_BACKENDS – there are different ones for each provider. For now, I’m just sticking with social_auth.backens.OpenIDBackend and django.contrib.auth.backends.ModelBackend.
- Set LOGIN_URL, LOGIN_REDIRECT_URL, and LOGIN_ERROR_URL.
- Set SOCIAL_AUTH_USERNAME_FIXER to determine the original username that users will get when they register.
- URLs:
url(r'auth/', include('social_auth.urls')),
Undocumented steps:
- Copy over templates from the included example project to get started, and add some views to use them. The example views are home (a sign in page), done (sign in complete page), and error.
How it looks
Navigating to the home view that I copied over from the example project:
Looks pretty good. I put in my OpenID, confirm at my OpenID provider, and then — oh no!
Strangely, if I repeat the process a second time changing nothing, I am successfuly authenticated. But after logging out, it again takes 2 tries, the first ending in a CSRF failure. I filed a bug and was heartened to get a response from the author within a few hours, though as yet no resolution. But the responsiveness tells me that this project is alive and well and that issues like this probably will get fixed.
Outcome
OpenID ended in a CSRF error. But at least the author responded quickly to a bug report.
django-allauth
django-allauth is the newer kid on the block — first commit was last October. It is also the only one which supports username/password/email registration as well as social registration. It seems like the closest match in spirit to omniauth. It looks like this project was born out of the Pinax mindset, and carries across some pinax-isms into its setup.
Installation
Documented steps:
- Settings:
TEMPLATE_CONTEXT_PROCESSORS = (
...
"allauth.context_processors.allauth",
"allauth.account.context_processors.account"
)
AUTHENTICATION_BACKENDS = (
...
"allauth.account.auth_backends.AuthenticationBackend",
)
INSTALLED_APPS = (
...
'emailconfirmation',
'uni_form',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.twitter',
'allauth.openid',
'allauth.facebook',
...
)
- URLs:
(r'^accounts/', include('allauth.urls')))
Undocumented steps:
- A full set of default templates is provided, but these require the presence of site_base.html which contains blocks named head_title and body.
- Requires django.core.context_processors.request in TEMPLATE_CONTEXT_PROCESSORS (template syntax errors stating that request is undefined are the symptom).
- If you enable the allauth.facebook and allauth.twitter apps, you have to log into admin and create App entries for them; otherwise you’ll get template errors from template tags that require that they exist.
How it looks
Navigating to /accounts/login/, we get this lovely screen:
Off the bat, google and yahoo authentication work as they should. I don’t have a hyves account so I didn’t test that. Clicking OpenID takes you to this secondary screen:
It looks as though it would be pretty straight-forward to make that load in a nice ajaxy way, or from one page, as well.
And username/password registration works:
Outcome
I’ve gotta say, this one seems like a clear winner. It’s the only one that worked out of the box for me, and it appears to be straight-forward to customize it as needed. The sub-app method of adding providers leads to some slightly hackish noodliness under the hood for things like importing context processors, but it doesn’t look too bad.
Conclusion
django-allauth looks great. django-social-auth looks promising and usable, though I wasn’t able to test as far as I’d like.
All of them need more work with testing and documentation. In part, that’s where we, the developer community come in; but as a maintainer of minor open source projects myself, I know how much its incumbent on the maintainer to buckle down on that stuff if it’s ever going to get done. I’d hazard a guess that some testing and docs for django-allauth or the inclusion of username/password registration on django-social-auth could make one of them a clear winner that we could all put our weight behind. For now, I’m using django-allauth.