Creating a Custom Authentication Backend in Django
Tags: python, django, programming, webdev
Published: 2024-01-13 08:27:00

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...

```python
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.



```python
# 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](https://github.com/django/django/blob/a7c73b944f51d6c92ec876fd7e0a171e7c01657d/django/contrib/auth/backends.py#L8-L160).

##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

```bash
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!


```python
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.


```python
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🤗