Prerequisites

Before creating our application, you need to install python and pip.

This article gives the installation details on Mac and Linux.

We will also need pipenv to create a contained environment and avoid conflicts with other projects (more details on why using pipenv).

You can install it with this command: pip install pipenv —-user

Creation of the Django application

The first step is to create our contained pipenv environment and install Django and ldap3, a library to connect to an LDAP:

mkdir DjangoApplications && cd DjangoApplications

pipenv install Django ldap3

Now that we have a configured environment, we can create our Django application and initialize the database:

pipenv run django-admin startproject AutomaticDjangoAuthentication

cd AutomaticDjangoAuthentication

pipenv run python manage.py migrate

We have now a Django application called AutomaticDjangoAuthentication ready to be run.

Let’s launch it: pipenv run python manage.py runserver

The application is launched and you should be able to access the admin dashboard on http://localhost:8000/admin:

Django Admin login page

By default, Django uses a user-oriented authentication with a login page. To connect on the admin dashboard and add other users, we need to create a user with all the permissions (super user) using the createsuperuser command:

pipenv run python manage.py createsuperuser

You should be able to connect using the user you created and access the admin dashboard.

We can now customize the default Django authentication process to authenticate using LDAP and auto-create the users in the Django database.

Auto-create users in the Django database on login

The default Django authentication uses backend classes to authenticate the users. On login, each backend authenticate method is called by priority until a user is returned or no more backends are to try.

In order to have an interesting external authentication system, I used a public LDAP server shared by forumsys. It provides several users and different groups that make LDAP tests very easy (more details here).

I created a service to check the user can authenticate in the LDAP given a username and password:

Then we can create our custom authentication backend. We check that the user can access the LDAP (or your own authentication system).

If so, we create and return a Django user with the permissions we want him to have. Here I give him only the right to access the admin dashboard (see Django permissions):

The last step before trying our new login is to override the default configuration. To do this, we add our backend in the Django app settings file AutomaticDjangoAuthentication/settings.py:

Now we should be able to log in the admin dashboard with one of the LDAP users, newton, euler, einstein…, with the password password. 🎉

Automatic user login

With a single sign-on protocol implemented, like Kerberos in Active Directory, the users only have to log in once to be authenticated over the network. It is then not UX friendly to ask the users to log in another time.

To avoid that, we will use the Django middleware to automatically log in instead of displaying the login page.

Django uses several middlewares, which are defined with a priority in AutomaticDjangoAuthentication/settings.py, to ensure security, cookies, authentication and more.

Each middleware is a layer wrapping the view that can implement five hooks. Each hook is a step to manage the request, display the view and manage the response and potential exceptions (more details in the Django documentation). In the schema below you can see in which order the hooks are called in Django 1.7:

* The process_request and process_response hooks have been removed in Django 2.2 (see the details here).

Middleware order of execution from Django doc

We will then add a new middleware after the AuthenticationMiddleware to automatically authenticate the users:

It is very basic!

We try to authenticate the user if he is not already authenticated.

Then we either log in the user in the app or return a Forbidden response if the user cannot be authenticated.

As with the backend, we have to add the middleware to the Django configuration file AutomaticDjangoAuthentication/settings.py:

To simplify this article, I did not implement a complex Kerberos server. Instead, I used query strings to pass the user’s credentials.

We must then modify our custom authentication backend to get the query strings from the request:

Now you can try to access the admin dashboard

…

And receive a 403 Forbidden! 🤯 (if you’re already logged in, log out and retry)

That’s because you need to send your credentials as query strings to be authenticated.

Let’s try again with http://localhost:8000/admin/?username=euler&password=password … Magic, it works 🎉

We can now authenticate each user without going through the login page.

How to implement it with Active Directory

A concrete implementation would not use query strings. Instead, we would use the external authentication system to get the users’ information. For example, I integrated this solution with Active Directory.

Active Directory is a Microsoft solution that uses the LDAP protocol and the Kerberos single sign-on protocol:

LDAP permits accessing and storing information on the users.

Kerberos permits to securely authenticate the users through the network using encrypted tokens (more explanations in this video).

If you want to plug with an Active Directory Kerberos, you can follow this tutorial. Once you configured Apache, you will be able to access the user’s username in the request REMOTE_USER header.

With the username, you will then be able to fetch the user information from the Active Directory LDAP and create the user in the Django database.