Auth tips and tricks

Published November 21, 2007. Filed under: Django.

Django’s bundled authentication framework is stupendously useful as an out-of-the-box solution for common user-authentication needs, and provides a ton of things basically for free: users, permissions, groups, login/logout functionality, middleware for automatically detecting the user’s identity, the ability to restrict views based on various attributes (including staff members, superusers, permissions and arbitrary tests), the list goes on and on. For probably 90% or more of the applications you’ll develop, the simple combination of the User model and the built-in login/logout views and middleware will take care of your needs just fine, but there’s a lot more functionality lurking in the auth framework, so today let’s take a look at some lesser-known (or just lesser-noticed) features of Django’s authentication framework, and some places where you can reach in and extend it for the cases where it doesn’t quite give you what you need.

More views than just login and logout

Everybody knows, or hopefully knows, that the auth system provides views for logging users in and out — django.contrib.auth.views.login and django.contrib.auth.views.logout, respectively — but there are a number of other views in django.contrib.auth which turn it into a much more robust system for handling users.

For example, users who need to change their passwords can be routed to django.contrib.auth.views.password_change, which — as the name implies — shows a form that prompts for the user’s existing password, then asks for a new password (repeated, to catch typos) and handles the change. And users who forget their passwords can get help via django.contrib.views.auth.password_reset, which will generate a new random password and send it to the email address associated with the user’s account.

If you’ve ever used the “login_required” decorator, you know that Django can keep a user from accessing a particular page until they’ve successfully logged in; the process works like this:

  1. If the user isn’t logged in, redirect them to the login page.
  2. Check their credentials.
  3. If they supply a correct username and password, log them and and redirect back to the original page.

Generally the login_required decorator is the easiest way to do this, but there’s also a very similar view: view django.contrib.auth.views.redirect_to_login which, as the name implies, redirects to the login page, and then to a URL of your choice following a successful login. If this sounds strange, consider that there are a number of cases where you have authentication needs which can’t be expressed by a simple test like login_required or permission_required; if your test isn’t something which can be expressed concisely by a decorator (say, because it needs access to other attributes of the request besides the user), you can implement a bit of logic in your view and then — if the user fails your test — return redirect_to_login and pass request.path as the URL to redirect back to.

Full documentation for these and all of the auth framework’s built-in views are available online in Django’s authentication documentation.

Ready-made forms

A lot of these views rely on various types of forms to help them get things done; the login view, for example, needs a login form, and the password-changing views need their own appropriate forms. These are defined in django.contrib.auth.forms, and — though they haven’t yet been ported to newforms, and so still use the old forms library — are perfectly accesible to your own code, meaning you can use them directly or subclass them to add functionality.

Django’s bundled comments system, for example, subclasses django.contrib.auth.forms.AuthenticationForm (the login form) and adds some fields to provide a form for comments which require registration; if the user isn’t logged in, they’ll see the username and password fields in addition to the comment fields. This technique is pretty easily extensible to your own applications, which means that any time you want to let a user take some action only if they’re logged in, you can subclass AuthenticationForm to get the login fields and logic for free.

Several other ready-made forms are provided as well, and are briefly listed in the documentation.

Messages

Django’s bundled admin application displays helpful messages at the top of the page each time you take some action; things like ‘The user “evil_hacker” was successfully deleted” will pop up each time you add, change or delete an object through the admin. These are implemented via the model django.contrib.auth.models.Message, which lets you specify short messages for a specific (authenticated) user; a built-in context processor will then fetch them for you on every view so you can display them just as easily as the admin does. And using the messages system is about as simple as can be; in a view, request.user.message_set.create() will take a string and create a message for the currently-logged-in user.

Be aware, though, that there have been some proposals to change the behavior of Message so that it’s tied to sessions instead of authenticated users, which would broaden it to work with any visitor to your site regardless of whether they’re logged in or not; this may or may not end up happening in the future.

Extending the authentication system

If you need a few extra fields for your users, Django provides a built-in mechanism for specifying a “profile” model related to User; I’ve already written about this in some detail, and the auth chapter in the Django book also has documentation on this feature.

If you need to move beyond the built-in username/password authentication system based on values stored in the User model, that’s easy too; Django allows you to specify multiple authentication “backends”, and the User model’s username/password pair is just the default. Writing custom authentication backends is easy, and provides a clean way to integrate Django with an existing auth system. Several useful examples are floating around on the Web, including an LDAP backend and a FreeRADIUS backend, and can help you get a feel for how you can build your own custom authentication handlers for use with Django.

Additional features working with the auth system

There are also several third-party efforts to develop applications which complement or enhance Django’s built-in auth system; my own django-registration app, for example, provides a simple and fairly extensible mechanism for handling user signup, and integrates closely with the existing auth features wherever possible. And there’s been a ton of interest in Django/OpenID implementations, including several third-party applications and a dedicated mailing list.

And that’s just the tip of the iceberg

Everything I’ve covered here is documented in some fashion, but once they get login/logout and the middleware down, most people don’t seem to really dig in to the auth system and discover all that it has to offer. The authentication docs are pretty well-written, and provide one of the larger and more comprehensive pieces of documentation Django has, so if you’re using django.contrib.auth you really owe it to yourself to give it a thorough read. There are also a lot of useful tips and tricks floating around in blog entries and mailing-list discussions, all of which can be helpfully found by Google searches for various relevant terms (such as “django auth backend” or “extend django auth”). So don’t be fooled by the idea that the auth system is “just” the User model and login/logout views; there’s a ton of useful functionality, and several major points for extensions to “hook in”, all of which can save you a ton of time and effort as you get your applications off the ground.