Creating a Custom Authentication Backend in Django

So you want to authenticate different types of users in different ways, or maybe you want to integrate your system with third-party authentication services like Firebase or Auth0, Whatever your use case maybe if you require a custom way of authenticating a user in Django then writing a custom authentication backend can be the right way to go.

What are Django authentication backends

An authentication backend is a class that implements two required methods: get_user(user_id) and authenticate(request, **credentials) as well as a set of optional permission related authorization methods.

If we take a look at part of the default authentication backend...

class ModelBackend(BaseBackend):
    """
    Authenticates against settings.AUTH_USER_MODEL.
    """    
    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.get_by_natural_key(username)
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user =  User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None

As you can see the authenticate method takes in a username and a password attribute and proceeds to authenticate the user, user_can_authenticate checks if the user is active, After all checks have passed the user will be returned.

Note: The username field is not necessarily the username field in the User model or the DB table but rather the USERNAME_FIELD variable you declare in your Model.

# The default authentication backend will use the email as username
USERNAME_FIELD = 'email'

So if you are just looking to authenticate all your users based on another field just use this setting.

I always find it quite nice to read Django's implementation to understand how it works and to understand how to better utilize it together with the documentation, blogs and tutorial videos, I advise all of you to do the same🙂, You can find Django's full authentication backend implementation here.

Implementing a custom authentication backend in only 2 steps🔥

Step 0: Spin up a new Django project

While I won't be showing you how to setup an environment, it's advised you use them when working on Python projects

django-admin startproject custom_auth
django-admin startapp auth

Step 1: Implementing the backend code

Hold up.. before you proceed to copy-paste the code😅, Create a "backends" folder and in it an AuthBackends.py file. Now you can copy-paste!

class StaffIDAuthBackend(ModelBackend): #inherit from ModelBacken
    def authenticate(self, request, staff_id=None, password=None, **kwargs):
        if staff_id is None or password is None:
            return
        try:
            user = User.objects.get(staff_id=staff_id)
        except User.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            User().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user =  User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None

We have created an authentication backend for letting staff members log in to our system via their unique staff_id.

Note: The field you choose to authenticate your user against needs to be unique or you need to pair it with another field that is in conjunction unique when used together with the staff_id field.

Step 2: Telling Django we want to use our backend

In Django, when you want to use a custom backend for authentication, you need to configure it in your project's settings. We need to declare it in the AUTHENTICATION_BACKENDS variable.

You won't need to import the class into the settings.py file rather you declare an absolute path starting from the app.

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'Users.backends.AuthBackends.StaffIDAuthBackend',
    # format: app.go.through.the.folders.file.Class
]

Now Django will use the Auth backend automatically!

It's worth noting that during authentication Django goes through the list from top to bottom trying to authenticate against each backend until one works or all fail before the process fails altogether so order your auth model backends according to your specifications.

Conclusion

In this blog post, we have gone through the process of creating a custom Django authentication backend and also took a pick at how Django implements its auth backend, I hope you gained a little knowledge from this blog post, Cheers🤗