DEV Community

Cover image for Using Custom Authentication Backends in Django
Aaron Harris for Kite

Posted on • Originally published at kite.com

Using Custom Authentication Backends in Django

Many organizations use widely-adopted authentication systems provided by services like Google, Facebook, or GitHub. A few Python packages provide authentication integration with these services, but most of them expect you to be handling the final user accounts on with Django. What happens when you need to work with user accounts that live in another system altogether?

In this article, you’ll see the interface that Django exposes for authenticating to an external system. By the end, you should understand the pieces involved in mapping an external system’s information to Django’s native User objects in order to work with them on your own site.

Django’s default authentication

In the Django User Authentication System, we covered the basics of how default authentication works in Django. Ultimately, you can interact with User objects and understand if a user is_authenticated or not. Using the default authentication system, you can make use of many of Django’s built-in features like its login and logout views and password reset workflow.

When working with an external authentication system, you have to manage these pieces yourself. Some of them may not make sense to you depending on how your authentication system works.

Authentication backends

As with many of Django’s systems, authentication is modeled as a plugin system. Django will try to authenticate users through a series of authentication backends. The default backend checks a user’s username and password against all the existing User objects in the database to authenticate them. The AUTHENTICATION_BACKENDS setting is your entrypoint to intercept this workflow and point Django to your external system.

An authentication backend is a class that, minimally, implements two methods:

  • get_user(user_id) — a user_id can be whatever unique identifier your external system uses to distinguish users, and get_user returns either a user object matching the given user_id or None.
  • authenticate(request, **credentials) — the request is the current HTTP request, and the credentials keyword arguments are whatever credentials your external system needs to check if a user should be authenticated or not. This is often a username and password, but it could be an API token or some other scheme. authenticate returns an authenticated User object or None.

Inside your authentication backend’s authenticate method, you can pass along the credentials to your external system via a REST API or another common authentication scheme like LDAP or SAML.

Using the wonderful Yes or No? API, you could build an authentication backend that authenticates a user occasionally if the API permits:

import requests

class FickleAuthBackend:
    def authenticate(self, request, username):
        response = requests.get(
            'https://yesno.wtf/api/'
        ).json()
        return User(username=username, password='') if response['answer'] == 'yes' else None

While authenticate can return a user object or None, it may also return an AnonymousUser object, or raise PermissionDenied to explicitly halt any further authentication checks. This allows for a variety of ways to proceed, and anonymous users may still have certain permissions. You’ll want to account for that in your middleware and views.

If the external user service provides additional information about the user, get_user might be a good place to grab some of that data. You can add attributes to the user object in authenticate before you return it if you’d like, but be careful of how many attributes you add dynamically.

Permissions

I also covered Django’s permission scheme in The Django User Authentication System: when given a user, you can inquire about their permissions generally or against specific objects using the has_perm method. Custom authentication backends can override permission checking methods and Django will check against those first before falling back to its default checks. This allows you to make queries to your external system about permissions in addition to authentication:

... continue with Permissions see the code, and more Django tutorials by Dane!

Dane Hillard has an upcoming book "Practices of the Python Pro" coming this month (October 2019)

Top comments (2)

Collapse
 
sibi profile image
sibi

explained it very well, Aaron. Thanks. Keep writing

Collapse
 
alphaharris profile image
Aaron Harris

Thanks! Our buddy Dane gets the credit for this one. I'll let him know!