Hi, I'm Harlin and welcome to my blog. I write about Python, Alfresco and other cheesy comestibles.

Python + Django - How to use the built-in authentication system

In this article I want to show you how to use the built-in authentication system that Django provides for you out of the box. You could build your own authentication functionality into a view by doing the following:

  1. Check to see if the request.user is authenticated.
  2. If the user is not, then you can redirect him to a page that asks for username and password.
  3. When the user comes back to your view, you could check that the username and password are correct and then set some kind of authentication token throughout the user's session.

If you didn't know any better, you might think this is how it's done. Which it mostly is and if you were using other platforms, like Java and PHP, you could do very much the same. But, it would still be very tedious. Even after creating the boilerplate code to facilitate this, you would need to incorporate it into every view that required authentication. You could shorten the process by calling a function that took care of this but that would render your code a bit unreadable.

Thankfully, Django provides this kind of functionality out of the box (to be fair, so do frameworks for other languages). It only requires a few steps of setup.

So, first of all, we'll want to set up a very simple Hello World Django project.

Go to a directory to create the project. If you don't already have pyenv installed, I highly recommend it. You can read about installing it here. I would recommend installing Python 3.6.4 if you don't have it already.

Build simple Django project that shows index page.

# pyenv global 3.6.4
# pyenv virtualenv deploy_django 
# pyenv virtualenv auth_demo
# pyenv local auth_demo

You will need to only install the Django package:

# pip install django
# django-admin.py startproject authdemo
# cd authdemo/
# ./manage.py startapp web

In case you're wondering, the versions of the packages I used are:

Django==2.0.2

Open authdemo/settings.py and add 'web' to the end of INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    'web',
]

We'll go ahead and build the tables for our SQLite3 database by running these commands:

# ./manage.py makemigrations
# ./manage.py migrate

Optionally, you can add an admin user with manage.py createsuperuser:

# ./manage.py createsuperuser

Inside the "web" application, create a folder called "templates":

# mkdir web/templates

Inside web/templates create a file called index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Hello World!</title>
</head>
<body>
    <p>Hello World!</p>
</body>
</html>

Open web/views.py and add:

from django.shortcuts import render


def index(request):
    return render(
        request,
        'index.html',
        {},
    )

Next, open authdemo/urls.py and add:

from django.contrib import admin
from django.urls import path
from web.views import index


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index),
]

Now, let's do our first smoke test to make sure Django is running as expected:

# ./manage.py runserver

Open http://localhost:8000/ and we should see a web page that simply says "Hello World!".

If it's not, or you get errors, go back through the steps until this point and make you get them right before moving on to the next part.

Build login part

Now, it's time to build the login and logout functionality.

We're going to need a setting to tell Django to redirect itself to the "/" page once a user authenticates. If you were to go directly to http://localhost:8000/login/ and we did not set LOGIN_REDIRECT_URL to '/', this would default our navigation to /accounts/profile which is not what we want in any event. So, open authdemo/settings.py and add at the end:

LOGIN_REDIRECT_URL = '/'

Next, let's change our index view a bit. Open web/views.py and make sure the code is the following:

from django.contrib.auth.decorators import login_required
from django.shortcuts import render


@login_required(login_url='/login/')
def index(request):
    return render(
        request,
        'index.html',
        {},
    )

Note that all we did was import the "@login_required" directive and used it above our index view. This enforces authentication for the view. We also told it to use "/login/" as our login url.

Now, let's configure our url routes to handle the login and logout views:

Open authdemo/urls.py and make sure it has the following code:

from django.contrib import admin
from django.contrib.auth.views import login, logout
from django.urls import path
from web.views import index


urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', login, {'template_name': 'login.html'}, name='login'),
    path('logout/', logout, {'next_page': '/login/?next=/'}, name='logout'),
    path('', index),
]

Let's create our login template called login.html. This will make up the login page so that we can enter our username and password. Open templates/login.html and add:

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <form method="post">

        {% csrf_token %}

        {{ form.as_p }}

        <button type="submit">
            Login
        </button>
    </form>
</body>
</html>

Let's go back to our index page and make some changes. With the following changes, our output will now say "Hello admin!" assuming we're going to use the admin user to log in with that was created with ./manage.py superuser earlier. Open templates/index.html and add:

<!DOCTYPE html>
<html>
<head>
    <title>Hello World!</title>
</head>
<body>
    <p>Hello {{ request.user.username }}!</p>
    <p>
        <a href="/logout/">
            Log Out
        </a>
    </p>
</body>
</html>

Ok. Ready for the moment of truth. Open your web browser to http://localhost:8000 and notice that it should redirect you to http://localhost:8000/login/?next=/. If it did, so far, so good.

Go ahead and enter your admin user and password and click Login.

This should now redirect you to http://localhost:8000 where you should be greeted with:

Hello admin!
Log Out

Go ahead and click the Log Out link. It will now log you out and redirect you back to "/login/" page.

Note that this is not simply a redirect to another page. It actually removes the user's attributes and properties from the current session. Even if you could access the index page, the request.user object would not be available and the app would not know who you are. Having the @login_required decorator provides more assurance that an unauthorized user would not be able to access that page anyway.

Django does make using authentication a snap once it's set up. But, there are a number of steps that have to take place before we can use it. Use this page as a note to refer back to when needed.

Hopefully this was helpful to you!

Any Comments, Always Welcome!