Django Authentication to use both email and username

Viewed 114

I've been nearly done with my django-react app with all the models, serializers, and APIs. But now I need to change the authentication method to also use email.

class User(AbstractUser):
   id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
   email = models.EmailField(unique=True)

   # Notice there is no username field, because it is included in AbstractUser

I've looked through some possible solutions but they involve using AbstractBaseUser while some other write a custom authentication backend that omits the username completely. This might break other views and frontend since we have been mainly using username.

I still want to keep the username and use both username and email to authenticate.

Is there any simple idea (preferably kept using AbstractUser) that I wouldn't have to make major change?

1 Answers

This code is taken from the book “Django 3 by Example”, chapter 4, section “Building a custom authentication backend”.

Django provides a simple way to define your own authentication backends. An authentication backend is a class that provides the following two methods: authenticate() and get_user().

Suppose you have the account app in your project to manage the authentication logic. You can create the authentication.py file with the following code:

class EmailAuthBackend(object):
    """
    Authenticate using an e-mail address.
    """
    def authenticate(self, request, username=None, password=None):
        try:
            user = User.objects.get(email=username)
            if user.check_password(password):
                return user
            return None
        except User.DoesNotExist:
            return None

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

The preceding code works as follows:

  • authenticate(): You try to retrieve a user with the given email address and check the password using the built-in check_password() method of the user model. This method handles the password hashing to compare the given password with the password stored in the database.
  • get_user(): You get a user through the ID provided in the user_id parameter. Django uses the backend that authenticated the user to retrieve the User object for the duration of the user session.

Edit the settings.py file of your project and add the following setting:

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'account.authentication.EmailAuthBackend',
]

In the preceding setting, you keep the default ModelBackend that is used to authenticate with the username and password and include your own email-based authentication backend.

Django will try to authenticate the user against each of the backends, so now you should be able to log in seamlessly using your username or email account. User credentials will be checked using the ModelBackend authentication backend, and if no user is returned, the credentials will be checked using your custom EmailAuthBackend backend.

Related