Using the Django authentication framework

Django comes with a built-in authentication framework that can handle user authentication, sessions, permissions, and user groups. The authentication system includes views for common user actions such as login, logout, change password, and reset password.

The authentication framework is located at django.contrib.auth and is used by other Django contrib packages. Remember that you already used the authentication framework in Chapter 1, Building a Blog Application to create a superuser for your blog application to access the administration site.

When you create a new Django project using the startproject command, the authentication framework is included in the default settings of your project. It consists of the django.contrib.auth application and the following two middleware classes found in the MIDDLEWARE_CLASSES setting of your project:

  • AuthenticationMiddleware: Associates users with requests using sessions
  • SessionMiddleware: Handles the current session across requests

A middleware is a class with methods that are globally executed during the request or response phase. You will use middleware classes on several occasions throughout this book. You will learn to create custom middlewares in Chapter 13, Going Live.

The authentication framework also includes the following models:

  • User: A user model with basic fields; the main fields of this model are: username, password, email, first_name, last_name, and is_active.
  • Group: A group model to categorize users.
  • Permission: Flags to perform certain actions.

The framework also includes default authentication views and forms that we will use later.

Creating a log-in view

We will start by using the Django authentication framework to allow users to log in into our website. Our view should perform the following actions to log in a user:

  1. Get the username and password by posting a form.
  2. Authenticate the user against the data stored in the database.
  3. Check if the user is active.
  4. Log the user into the website and start an authenticated session.

First, we are going to create a log in form. Create a new forms.py file into your account application directory and add the following lines to it:

from django import forms

class LoginForm(forms.Form):
    username = forms.CharField()
    password = forms.CharField(widget=forms.PasswordInput)

This form will be used to authenticate users against the database. Note that we use the PasswordInput widget to render its HTML input element, including a type="password" attribute. Edit the views.py file of your account application and add the following code to it:

from django.http import HttpResponse
from django.shortcuts import render
from django.contrib.auth import authenticate, login
from .forms import LoginForm

def user_login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            user = authenticate(username=cd['username'],
                                password=cd['password'])
            if user is not None:
                if user.is_active:
                    login(request, user)
                    return HttpResponse('Authenticated '
                                        'successfully')
                else:
                    return HttpResponse('Disabled account')
            else:
                return HttpResponse('Invalid login')
    else:
        form = LoginForm()
    return render(request, 'account/login.html', {'form': form})

This is what our basic log in view does: when the user_login view is called with a GET request, we instantiate a new log in form with form = LoginForm() to display it in the template. When the user submits the form via POST, we perform the following actions:

  1. Instantiate the form with the submitted data with form = LoginForm(request.POST).
  2. Check if the form is valid. If it is not valid, we display the form errors in our template (for example, if user did not fill one of the fields).
  3. If the submitted data is valid, we authenticate the user against the database by using the authenticate() method. This method takes a username and a password and returns a User object if the user has been successfully authenticated, or None otherwise. If the user has not been authenticated, we return a raw HttpResponse displaying a message.
  4. If the user was successfully authenticated, we check if the user is active accessing its is_active attribute. This is an attribute of Django's User model. If the user is not active, we return an HttpResponse displaying the information.
  5. If the user is active, we log the user into the website. We set the user in the session by calling the login() method and return a success message.

Note

Note the difference between authenticate and login: authenticate() checks user credentials and returns a user object if they are right; login() sets the user in the current session.

Now, you need to create an URL pattern for this view. Create a new urls.py file into your account application directory and add the following code to it:

from django.conf.urls import url
from . import views

urlpatterns = [
    # post views
    url(r'^login/$', views.user_login, name='login'),
]

Edit the main urls.py file located in your bookmarks project directory and include the URL patterns of the account application as follows:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^account/', include('account.urls')),
]

The log in view can now be accessed by a URL. It is time to create a template for this view. Since you don't have any templates for this project, you can start by creating a base template that can be extended by the log in template. Create the following files and directories inside the account application directory:

templates/
    account/
        login.html
    base.html

Edit the base.html file and add the following code to it:

{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
  <title>{% block title %}{% endblock %}</title>
  <link href="{% static "css/base.css" %}" rel="stylesheet">
</head>
<body>
  <div id="header">
    <span class="logo">Bookmarks</span>
  </div>
  <div id="content">
    {% block content %}
    {% endblock %}
  </div>
</body>
</html>

This will be the base template for the website. As we did in our previous project, we include the CSS styles in the main template. You can find these static files in the code that comes along with this chapter. Copy the static/ directory of the account application to the same location in your project, so that you can use the static files.

The base template defines a title and a content block that can be filled with content by the templates that extend from it.

Let's create the template for our log-in form. Open the account/login.html template and add the following code to it:

{% extends "base.html" %}

{% block title %}Log-in{% endblock %}

{% block content %}
  <h1>Log-in</h1>
  <p>Please, use the following form to log-in:</p>
  <form action="." method="post">
    {{ form.as_p }}
    {% csrf_token %}
    <p><input type="submit" value="Log-in"></p>
  </form>
{% endblock %}

This template includes the form that is instantiated in the view. Since our form will be submitted via POST, we include the {% csrf_token %} template tag for CSRF protection. You learned about CSRF protection in Chapter 2, Enhancing Your Blog with Advanced Features.

There are no users in your database yet. You will need to create a superuser first in order to be able to access the administration site to manage other users. Open the command line and execute python manage.py createsuperuser. Fill in the desired username, e-mail, and password. Then run the development server using the command python manage.py runserver and open http://127.0.0.1:8000/admin/ in your browser. Access the administration site using the username and password of the user you just created. You will see the Django administration site, including the User and Group models of the Django authentication framework. It will look as follows:

Creating a log-in view

Create a new user using the administration site and open http://127.0.0.1:8000/account/login/ in your browser. You should see the rendered template, including the log-in form:

Creating a log-in view

Now, submit the form leaving one of the fields empty. In this case, you will see that the form is not valid and it displays errors as follows:

Creating a log-in view

If you enter an non-existing user or a wrong password, you will get an Invalid login message.

If you enter valid credentials, you get an Authenticated successfully message like this:

Creating a log-in view

Using Django authentication views

Django includes several forms and views in the authentication framework that you can use straightaway. The login view you have created is a good exercise to understand the process of user authentication in Django. However, you can use the default Django authentication views in most cases.

Django provides the following views to deal with authentication:

  • login: Handles a log in form and logs in a user
  • logout: Logs out a user
  • logout_then_login: Logs out a user and redirects him to the log-in page

Django provides the following views to handle password changes:

  • password_change: Handles a form to change user password
  • password_change_done: The success page shown to the user after changing his password

Django also includes the following views to allow users to reset their password:

  • password_reset: Allows the user to reset his password. It generates a one-time use link with a token and sends it to the user's e-mail account.
  • password_reset_done: Shows the user that the e-mail to reset his password has been sent to his e-mail account.
  • password_reset_confirm: Lets the user set a new password.
  • password_reset_complete: The success page shown to the user after he resets their password.

The views listed here can save you a lot of time when creating a website with user accounts. The views use default values that you can override, such as the location of the template to be rendered or the form to be used by the view.

You can get more information about built-in authentication views at https://docs.djangoproject.com/en/1.8/topics/auth/default/#module-django.contrib.auth.views.

Log in and log out views

Edit the urls.py of your account application and make it look like this:

from django.conf.urls import url
from . import views

urlpatterns = [
    # previous login view
    # url(r'^login/$', views.user_login, name='login'), 

    # login / logout urls
    url(r'^login/$',
        'django.contrib.auth.views.login',
        name='login'),
    url(r'^logout/$',
        'django.contrib.auth.views.logout',
        name='logout'),
    url(r'^logout-then-login/$', 
        'django.contrib.auth.views.logout_then_login', 
        name='logout_then_login'),
]

We comment out the URL pattern for the user_login view we have created previously to use the login view of Django's authentication framework.

Create a new directory inside the templates directory of your account application and name it registration. This is the default path where the Django authentication views expect your authentication templates to be. Create a new file inside the new directory, name it login.html, and add the following code to it:

{% extends "base.html" %}

{% block title %}Log-in{% endblock %}

{% block content %}
  <h1>Log-in</h1>
  {% if form.errors %}
    <p>
      Your username and password didn't match. 
      Please try again.
    </p>
  {% else %}
    <p>Please, use the following form to log-in:</p>
  {% endif %}
  <div class="login-form">
    <form action="{% url 'login' %}" method="post">
      {{ form.as_p }}
      {% csrf_token %}
      <input type="hidden" name="next" value="{{ next }}" />
      <p><input type="submit" value="Log-in"></p>
    </form>
  </div>
{% endblock %}

This login template is quite similar to the one we created before. Django uses the AuthenticationForm located at django.contrib.auth.forms by default. This form tries to authenticate the user and raises a validation error if the login was not successful. In this case, we can look for errors using {% if form.errors %} in the template to check if the provided credentials are wrong. Notice that we have added a hidden HTML <input> element to submit the value of a variable called next. This variable is first set by the log in view when you pass a next parameter in the request (for example. http://127.0.0.1:8000/account/login/?next=/account/).

The next parameter has to be a URL. If this parameter is given, the Django login view will redirect to the given URL after the user logs in.

Now, create a logged_out.html template inside the registration template directory and make it look like this:

{% extends "base.html" %}

{% block title %}Logged out{% endblock %}

{% block content %}
  <h1>Logged out</h1>
  <p>You have been successfully logged out. You can <a href="{% url "login" %}">log-in again</a>.</p>
{% endblock %}

This is the template that Django will display after the user logs out.

After adding the URL patterns and the templates for log in and log out views, your website is ready for users to log in using the Django authentication views.

Note

Note that the logout_then_login view we included in our urlconf does not need any template since it performs a redirect to the log in view.

Now, we are going to create a new view to display a dashboard to the user when he or she logs in their account. Open the views.py file of your account application and add the following code to it:

from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request,
                  'account/dashboard.html',
                  {'section': 'dashboard'})

We decorate our view with the login_required decorator of the authentication framework. The login_required decorator checks if the current user is authenticated. If the user is authenticated, it executes the decorated view; if the user is not authenticated, it redirects him to the login URL with the URL he was trying to access as a GET parameter named next. By doing so, the log in view redirects the user back to the URL he was trying to access after he is successfully logged in. Remember that we added a hidden input in the form of our log in template for this purpose.

We also define a section variable. We are going to use this variable to track which section of the site the user is watching. Multiple views may correspond to the same section. This is a simple way to define which section each view corresponds to.

Now, you need to create a template for the dashboard view. Create a new file inside the templates/account/ directory and name it dashboard.html. Make it look like this:

{% extends "base.html" %}

{% block title %}Dashboard{% endblock %}

{% block content %}
  <h1>Dashboard</h1>
  <p>Welcome to your dashboard.</p>
{% endblock %}

Then, add the following URL pattern for this view in the urls.py file of the account application:

urlpatterns = [
    # ...
    url(r'^$', views.dashboard, name='dashboard'),
]

Edit the settings.py file of your project and add the following code to it:

from django.core.urlresolvers import reverse_lazy

LOGIN_REDIRECT_URL = reverse_lazy('dashboard')
LOGIN_URL = reverse_lazy('login')
LOGOUT_URL = reverse_lazy('logout')

These settings are:

  • LOGIN_REDIRECT_URL: Tells Django which URL to redirect after login if the contrib.auth.views.login view gets no next parameter
  • LOGIN_URL: Is the URL to redirect the user to log in (e.g. using the login_required decorator)
  • LOGOUT_URL: Is the URL to redirect the user to log out

We are using reverse_lazy() to build the URLs dynamically by their name. The reverse_lazy() function reverses URLs just like reverse() does, but you can use it when you need to reverse URLs before your project's URL configuration is loaded.

Let's summarize what you have done so far:

  • You added the built-in Django authentication log in and log out views to your project
  • You created custom templates for both views and defined a simple view to redirect users after they log in
  • Finally, you configured your settings for Django to use these URLs by default

Now, we are going to add log in and log out links to our base template to put everything together.

In order to do this, we have to determine whether the current user is logged in or not, to display the appropriate link in each case. The current user is set in the Http Request object by the authentication middleware. You can access it with request.user. You will find a user object in the request even if the user is not authenticated. A non-authenticated user is set in the request as an instance of AnonymousUser. The best way to check if the current user is authenticated is by calling request.user.is_authenticated().

Edit your base.html and modify the <div> element with ID header, like this:

<div id="header">
  <span class="logo">Bookmarks</span>
  {% if request.user.is_authenticated %}
    <ul class="menu">
      <li {% if section == "dashboard" %}class="selected"{% endif %}>
        <a href="{% url "dashboard" %}">My dashboard</a>
      </li>
      <li {% if section == "images" %}class="selected"{% endif %}>
        <a href="#">Images</a>
      </li>
      <li {% if section == "people" %}class="selected"{% endif %}>
        <a href="#">People</a>
      </li>
    </ul>
  {% endif %}
    
  <span class="user">
    {% if request.user.is_authenticated %}
      Hello {{ request.user.first_name }},
      <a href="{% url "logout" %}">Logout</a>
    {% else %}
      <a href="{% url "login" %}">Log-in</a>
    {% endif %}
  </span>
</div>

As you can see, we only display the site's menu to authenticated users. We also check the current section to add a selected class attribute to the corresponding <li> item in order to highlight the current section in the menu using CSS. We also display the user's first name and a link to log out if the user is authenticated, or a link to log in otherwise.

Now, open http://127.0.0.1:8000/account/login/ in your browser. You should see the log in page. Enter a valid username and password and click the Log-in button. You should see something like this:

Log in and log out views

You can see that the My dashboard section is highlighted with CSS because it has a selected class. Since the user is authenticated the first name of the user is displayed in the right side of the header. Click on the Logout link. You should see the following page:

Log in and log out views

In this page, you can see that the user is logged out, and therefore, you cannot see the menu of the website anymore. The link on the right side of the header is shows now Log-in.

If you are seeing the log out page of the Django administration site instead of your own log out page, check the INSTALLED_APPS setting of your project and make sure that django.contrib.admin comes after the account application. Both templates are located in the same relative path and the Django template loader will use the first one it finds.

Change password views

We also need our users to be able to change their password after they log in to our site. We are going to integrate Django authentication views for password change. Open the urls.py file of the account application and add the following URL patterns to it:

# change password urls
url(r'^password-change/$',
    'django.contrib.auth.views.password_change',
    name='password_change'),
url(r'^password-change/done/$',
    'django.contrib.auth.views.password_change_done',
    name='password_change_done'),

The password_change view will handle the form to change the password and the password_change_done will display a success message after the user has successfully changed his password. Let's create a template for each view.

Add a new file inside the templates/registration/ directory of your account application and name it password_change_form.html. Add the following code to it:

{% extends "base.html" %}

{% block title %}Change you password{% endblock %}

{% block content %}
  <h1>Change you password</h1>
  <p>Use the form below to change your password.</p>
  <form action="." method="post">
    {{ form.as_p }}
    <p><input type="submit" value="Change"></p>
    {% csrf_token %}
  </form>
{% endblock %}

This template includes the form to change the password. Now, create another file in the same directory and name it password_change_done.html. Add the following code to it:

{% extends "base.html" %}

{% block title %}Password changed{% endblock %}

{% block content %}
  <h1>Password changed</h1>
  <p>Your password has been successfully changed.</p>
{% endblock %}

This template only contains the success message to be displayed when the user has successfully changed their password.

Open http://127.0.0.1:8000/account/password-change/ in your browser. If your user is not logged in, the browser will redirect you to the login page. After you are successfully authenticated, you will see the following change password page:

Change password views

Fill in the form with your current password and your new password and click the Change button. You will see the following success page:

Change password views

Log out and log in again using your new password to verify that everything works as expected.

Reset password views

Add the following URL patterns for password restoration to the urls.py file of the account application:

# restore password urls
url(r'^password-reset/$',
    'django.contrib.auth.views.password_reset',
    name='password_reset'),
url(r'^password-reset/done/$',
    'django.contrib.auth.views.password_reset_done',
    name='password_reset_done'),
url(r'^password-reset/confirm/(?P<uidb64>[-w]+)/(?P<token>[-w]+)/$',
    'django.contrib.auth.views.password_reset_confirm',
    name='password_reset_confirm'),
url(r'^password-reset/complete/$',
    'django.contrib.auth.views.password_reset_complete',
    name='password_reset_complete'),

Add a new file in the templates/registration/ directory of your account application and name it password_reset_form.html. Add the following code to it:

{% extends "base.html" %}

{% block title %}Reset your password{% endblock %}

{% block content %}
  <h1>Forgotten your password?</h1>
  <p>Enter your e-mail address to obtain a new password.</p>
  <form action="." method="post">
    {{ form.as_p }}
    <p><input type="submit" value="Send e-mail"></p>
    {% csrf_token %}
  </form>
{% endblock %}

Now, create another file in the same directory and name it password_reset_email.html. Add the following code to it:

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol }}://{{ domain }}{% url "password_reset_confirm" uidb64=uid token=token %}
Your username, in case you've forgotten: {{ user.get_username }}

This is the template that will be used to render the e-mail sent to the user to reset their password.

Create another file in the same directory and name it password_reset_done.html. Add the following code to it:

{% extends "base.html" %}

{% block title %}Reset your password{% endblock %}

{% block content %}
  <h1>Reset your password</h1>
  <p>We've emailed you instructions for setting your password.</p>
  <p>If you don't receive an email, please make sure you've entered the address you registered with.</p>
{% endblock %}

Create another template and name it password_reset_confirm.html. Add the following code to it:

{% extends "base.html" %}

{% block title %}Reset your password{% endblock %}

{% block content %}
  <h1>Reset your password</h1>
  {% if validlink %}
    <p>Please enter your new password twice:</p>
    <form action="." method="post">
      {{ form.as_p }}
      {% csrf_token %}
      <p><input type="submit" value="Change my password" /></p>
    </form>
  {% else %}
    <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
  {% endif %}
{% endblock %}

We check if the provided link is valid. Django reset password view sets this variable and puts it in the context of this template. If the link is valid, we display the user password reset form.

Create another template and name it password_reset_complete.html. Enter the following code into it:

{% extends "base.html" %}

{% block title %}Password reset{% endblock %}

{% block content %}
  <h1>Password set</h1>
  <p>Your password has been set. You can <a href="{% url "login" %}">log in now</a></p>
{% endblock %}

Finally, edit the registration/login.html template of the account application and add the following code after the <form> element:

<p><a href="{% url "password_reset" %}">Forgotten your password?</a></p>

Now, open http://127.0.0.1:8000/account/login/ in your browser and click the Forgotten your password? link. You should see the following page:

Reset password views

At this point, you need to add an SMTP configuration to the settings.py file of your project, so that Django is able to send e-mails. We have seen how to add e-mail settings to your project in Chapter 2, Enhancing Your Blog with Advanced Features. However, during development, you can configure Django to write e-mails to the standard output instead of sending them through an SMTP server. Django provides an e-mail backend to write e-mails to the console. Edit the settings.py file of your project and add the following line:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

The EMAIL_BACKEND setting indicates the class to use to send e-mails.

Go back to your browser, enter the e-mail address of an existing user, and click the Send e-mail button. You should see the following page:

Reset password views

Take a look at the console where you are running the development server. You will see the generated e-mail as follows:

IME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Subject: Password reset on 127.0.0.1:8000
From: webmaster@localhost
To: [email protected]
Date: Thu, 24 Sep 2015 14:35:08 -0000
Message-ID: <[email protected]>

Someone asked for password reset for email [email protected]. Follow the link below:
http://127.0.0.1:8000/account/password-reset/confirm/MQ/45f-9c3f30caafd523055fcc/
Your username, in case you've forgotten: zenx

The e-mail is rendered using the password_reset_email.html template we created earlier. The URL to reset your password includes a token that was generated dynamically by Django. Open the link in your browser. You should see the following page:

Reset password views

The page to set a new password corresponds to the password_reset_confirm.html template. Fill in a new password and click the Change my password button. Django creates a new encrypted password and saves it in the database. You will see a success page like this one:

Reset password views

Now, you can log in back to your account using your new password. Each token to set a new password can be used only once. If you open the link you received again, you will get a message telling you the token is invalid.

You have integrated the views of the Django authentication framework in your project. These views are suitable for most cases. However, you can create your own views if you need a different behavior.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset