Get Django Updates

Set up Django user authentication, account creation with email & password management

Problem

You want to allow users the ability to use their emails to register, log-in, log-off, change their password and reset their password.

Solution

Install and configure django-allauth to allow users to use classic Django accounts with email (django.contrib.auth.models.User). Customize django-allauth templates by creating a separate set of templates in your own Django project to override the default django-allauth templates. Manage django-allauth accounts directly from the Django admin just like classic Django accounts.

How it works

In the previous recipe we explored how to create users and use them to set up permission management in a Django application, but you might recall we either had to use a Django command line tool or the Django admin for everything. These techniques while valid are not intended and won't scale for end users that visit an application. If you plan to require credentials from end users, you need to provide a way for users to register, a way for users to log-in and log-off, as well as a way for users to remember and change their passwords.

There are many techniques to set up these authentication and account creation tasks on the end-user facing part of a Django application. Django itself has a built-in solution through the django.contrib.auth package, which requires a few configuration tasks and templates. In addition, there are various other third party Django packages that are built specifically for this purpose, that are either from scratch solutions or improve on the set of features in django.contrib.auth. For this recipe, I'll use the third party django-allauth package which in my experience is the most stable and feature rich option.

Install and setup django-allauth

Note Why use django-allauth ?

Although the django.contrib.auth package is the built-in solution for Django's user management, it's severely limited for most real world applications circa 2015, where things like social authentication are almost a requirement and asking users to provide a username -- and not their email -- is a very outdated practice.

In addition, because the django.contrib.auth package is built-in to Django, there are often assumptions made by other Django packages -- particularly third party ones -- that the django.contrib.auth package is used for the purpose of user management. This means that in order to maintain the biggest margin of user management compatibility, you should keep the django.contrib.auth package available.

At the time of this writing, from the various third party packages and potential solutions available for Django user management, the djano-allauth package offers the best features (e.g. social authentication and e-mail based users), as well as the best integration with the django.contrib.auth package.

You can install the Django django-allauth package, as follows: pip install django-allauth. Once you make the installation you'll need to modify your project's settings.py file as described in listing 1.

Listing 1 - Requirements for django-allauth in settings.py


# Ensure the 'request' context processor is enable in TEMPLATES
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                # Already defined Django-related contexts here
                # 'allauth' requires the request context processor
                'django.template.context_processors.request',
            ],
        },
    },
]

# Add the 'allauth' backend to AUTHENTICATION_BACKEND and do not remove ModelBackend
AUTHENTICATION_BACKENDS = (
    # Needed to login by username in Django admin and to ensure compatibility with other packages
    'django.contrib.auth.backends.ModelBackend',
    # 'allauth' specific authentication methods
    'allauth.account.auth_backends.AuthenticationBackend',
)

# Ensure the 'django.contrib.sites' is declared in INSTALLED_APPS
# And also add the allauth, allauth.account and allauth.socialaccount to INSTALLED_APPS

INSTALLED_APPS = (
    # Django sites framework is required
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
)
# Ensure the SITE_ID is defined 
SITE_ID = 1

# Ensure EMAIL_BACKEND is set so allauth can proceed to send confirmation emails
# Set to console for development/testing
EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'

# Custom allauth settings
# Use email as the primary identifier
ACCOUNT_AUTHENTICATION_METHOD = 'email' 
ACCOUNT_EMAIL_REQUIRED = True
# Make email verification mandatory to avoid junk email accounts
ACCOUNT_EMAIL_VERIFICATION = 'mandatory' 
# Eliminate need to provide username, as it's a very old practice
ACCOUNT_USERNAME_REQUIRED = False

In addition to the changes in listing 1 to settings.py, you'll also need to register the django-allauth URL entry points in the main urls.py file. Listing 2 illustrates the changes you need to make to the main urls.py file.

Listing 2 - Requirements for django-allauth in main urls.py

urlpatterns = [
    ...
    url(r'^accounts/', include('allauth.urls')),
    ...
]

As you can see in listing 2, the URL regular expression tells Django to mount the django-allauth access points under the accounts/ path. Keep in mind the accounts/ path is in-line with Django's various user management defaults. This includes LOGIN_URL which defaults to /accounts/login/, LOGOUT_URL which defaults to /accounts/logout/ and LOGIN_REDIRECT_URL which defaults to /accounts/profile/. This means if the application requires a user to log-in, the user is sent to the URL in LOGIN_URL, similarly after a log-out action a user is sent to the URL in LOGOUT_OUT and after a successful log-in he is sent to the URL in LOGIN_REDIRECT_URL. So django-allauth integrates out of the box with these defaults.

Note django-allauth doesn't implement a /accounts/profile/

At the time of this writing, the most recent version of django-allauth does not implement the /accounts/profile/ path. This means that after a successful log-in you'll get an error. To avoid this error you'll need to either implement the /accounts/profile/ view method and template or define/override the LOGIN_REDIRECT_URL variable in settings.py to an existing URL (e.g.LOGIN_REDIRECT_URL='/' so the user is re-directed to the home page after a successful log-in).

Finally, you need to do the following miscellaneous steps to ensure django-allauth runs correctly:

First log-in and log-out run with django-allauth

I'll assume you already have a Django user created from the past recipe, if not take look at the recipe Set up Django users, groups and integrate permission management into an application to create one.

Next, go to the URL /accounts/login/ and you'll see page like the one illustrated in Figure 1 -- don't worry about the aesthetics now, I'll show you how to change them later in the recipe. Introduce an email/password and click the 'Sign in' button, if the credentials are correct, you'll be re-directed by default to the /accounts/profile/ URL or the LOGIN_REDIRECT_URL value in settings.py.

Django log-in form
Figure 1.- Django log-in form

If the user is a superuser or staff, go directly to the /admin/ URL and you'll confirm you're able to access the Django admin directly, since you just logged-in using django-allauth.

Now that you're logged in, go to the URL /accounts/logout/ and you'll see page like the one illustrated in Figure 2. Click on the 'Sign Out' button to confirm logging out, after the click you'll be re-directed by default to the /accounts/profile/ URL or the LOGIN_REDIRECT_URL value in settings.py.

Django log-out form
Figure 2.- Django log-out form

Create a user with django-allauth

If you review the URL /accounts/login/ illustrated in Figure 1, you can see there's a 'Sign up' link that takes you to the URL /accounts/signup/. Click on the link or go directly to this URL and you'll see a page like the one illustrated in Figure 3.

Django create user form
Figure 3.- Django create user form

Fill out the form presented in Figure 3 and click on the 'Sign up' button, if the submission is successful you'll be re-directed by default to the /accounts/profile/ URL or the LOGIN_REDIRECT_URL value in settings.py.

When you setup django-allauth, among the values you were told to setup in listing 1 was EMAIL_BACKEND. This is because this last form submission sends a confirmation email to validate the new user's email. If you set up the EMAIL_BACKEND as described in listing 1, look at the application console and you should see an email like the one in listing 3. If you have another EMAIL_BACKEND definition, then check the appropriate location for a confirmation email.

Listing 3 - Confirm email for new user in django-allauth

MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Subject: [coffeehouse.com] Please Confirm Your E-mail Address
From: webmaster@localhost
To: cashier@coffeehouse.com
Date: Wed, 12 Aug 2015 00:57:50 -0000
Message-ID: <20150812005750.15177.32621@laptop>

Hello from coffeehouse.com!

You're receiving this e-mail because user cashier at example.com has given yours as an e-mail address to connect their account.

To confirm this is correct, go to http://localhost:8000/accounts/confirm-email/1aflixsbb6sn14hptkntiyrgvk1r0pppqsurx6fxrs6wmlb96u8y3gotxzep0qie/

Thank you from coffeehouse.com!
coffeehouse.com
-------------------------------------------------------------------------------

Because of the variable ACCOUNT_EMAIL_VERIFICATION = 'mandatory' in settings.py set in listing 1, if you attempt to log-in before clicking on the verification link, you'll get a screen like the one in Figure 4. Once you click on the verification link, you'll see a screen like the one in Figure 5 to confirm the user. When you click on the 'Confirm' button to make the final verification, you'll be sent to the /accounts/login/ page illustrated in Figure 6 with a small flash message indicating the account was confirmed.

Django email verification screen
Figure 4.- Django email verification screen
Django email confirmation screen
Figure 5.- Django email confirmation screen
Django email confirmation success screen
Figure 6.- Django email confirmation success screen

If you look closely at the message in Figure 5 you'll notice it says Please confirm that ... is an email address for user ..., this user refers to a django.contrib.auth.models.User, the same kind you created in the past recipe. So behind the scenes django-allauth creates a regular Django user and integrates with django-allauth features (e.g. email log-in, social authentication). This built-in integration is an excellent feature because you get all the benefits of django-allauth and get to keep Django's default user management where you can give the same user Django admin access, superuser & staff permissions, ability to belong to Django groups and URL permission management, among other things.

Note django-allauth conventions for Django user creation

To create a Django user -- the django.contrib.auth.models.User kind -- django-allauth uses an email's local part (i.e. anything before @) as the username handle. In case multiple emails with the same name proceed to create an account, django-allauth uses a digit to assign the username (e.g.nancy@coffeehouse.com=nancy, nancy@hotmail.com=nancy2, nancy@gmail.com=nancy3).

Reset and change user password with django-allauth

The most common management task required by users is often related to passwords, whether it's resetting their password because they forgot it or changing it for security reasons. django-allauth provides built-in support for both scenarios. If you go to the URL /accounts/password/reset/ you'll see page like the one illustrated in Figure 7, where a user can introduce his email to reset his password.

Django reset password
Figure 7.- Django reset password
Django reset password email sent
Figure 8.- Django reset password email sent

Once you introduce an email in the screen illustrated in Figure 7 and click on the 'Reset My Password' button, Django sends you to the confirmation page in Figure 8 and also sends an email like the one in listing 4 with a password reset link.

Listing 4 - Reset email for new password django-allauth

MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Subject: [coffeehouse.com] Password Reset E-mail
From: webmaster@localhost
To: nancy@coffeehouse.com
Date: Wed, 19 Aug 2015 03:05:09 -0000
Message-ID: <20150819030509.15780.98825@laptop>

Hello from coffeehouse.com!

You're receiving this e-mail because you or someone else has requested a password for your user account at coffeehouse.com.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.

http://localhost:8000/accounts/password/reset/key/5-44f-0f6fbf1251fd33ee4b40/

Thank you for using coffeehouse.com!
coffeehouse.com
-------------------------------------------------------------------------------

Next, when you click on the reset email link, you'll see a screen like the one in Figure 9 to introduce a new password. After you click on the 'Change password' button you'll see a password confirmation flash message like the one in Figure 10.

Django password change
Figure 9.- Django password change
Django password change confirmation
Figure 10.- Django password change confirmation

Another option available in django-allauth related to passwords is to allow a user to change his password while he's logged-in. If you go to the URL /accounts/password/change/ you'll see a screen like the one Figure 11 where you can can introduce a new password. After you click on the 'Change Password' button, you'll see a confirmation message on the same screen which is illustrated in Figure 12.

Django password change logged-in
Figure 11.- Django password change logged-in
Django password change confirmation logged-in
Figure 12.- Django password change confirmation logged-in

Add and change user email with django-allauth

To aid users in the possibility of changing their initial email sign-up address, django-allauth has a dedicated page to manage email addresses. If you go to the URL /accounts/email you'll see this page which is illustrated in Figure 13.

Django email management
Figure 13.- Django email management

As you can see in Figure 13, in addition to the possibility of adding other emails to the account, a user can also change his primary email, re-send a verification for an email or even remove an email associated with the account.

Change templates for django-allauth

The built-in django-allauth templates while functional are not intended for use on a live site as you can appreciate in the previous figures, but you can easily customize django-allauth templates by overriding these in your Django project. Depending on the level of customization you need, you can take one of two approaches.

If you want to replace the HTML that surrounds the django-allauth forms, you can take advantage that all the backing forms inherit their behavior from a template named base.html and their content in enclosed in {% block content %}{% block %}. This means you can create a template called base.html in your Django project with all the elements you wish (e.g. custom colors, header menu) and declare the {% block content %}{% block %} in it and the forms are rendered in this context -- if you've never used Django templates look at the recipe Create reusable Django templates.

If you want to fully customize the HTML including the forms themselves (e.g. mobile-friendly forms or some other deep change), you can provide overriding templates in your Django project. Because django-allauth relies on 18 HTML templates and 6 email templates, I recommend you copy the django-allauth default templates to your Django project and then modify them as you require. You should ensure the django-allauth templates are copied with their original accounts sub-folder that should be accessible under a DIRS value of the TEMPLATES variable in settings.py. Based on your Python/django-allauth installation, the location of the default templates can be found on a route like /lib/python2.7/site-packages/allauth/templates/.

Models and database tables behind django-allauth

Even though django-allauth leverages Django's default user model django.contrib.auth.models.User where possible (e.g. to store passwords), it does add a series of models to support its more advanced features. If you look at Figure 14 you'll see the Django admin with the newly integrated django-allauth models.

Django admin with models for django-allauth
Figure 14.- Django admin with models for django-allauth

The first set of models for django-allauth corresponds to accounts of which there are two models: email address and email confirmation. The email address model keeps track of emails, their association with usernames (i.e. django.contrib.auth.models.User models), the primary email status, as well as an email's verification status. The email confirmation model keeps track of email verification keys, as well as the creation and sent timestamps of the verification. It should be noted email addresses model records are stored in the account_emailaddress database table and email confirmation model records are stored in the account_emailconfirmation database table.

The second set of models for django-allauth corresponds to social accounts, which are used to allow Django authentication from social sites (e.g.Facebook, Google, Twitter). If you followed the exercises in this recipe from the start, you will have noticed there's no trace of social account authentication. This is because the django-allauth basic set up process does not include social account authentication, even though the social accounts models are installed. In the next recipe, I describe how to setup Django social authentication with django-allauth.