Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django using email authentication with djoser for login

So i tried using djoser recently and i want to use email instead of username to login.

Djoser : http://djoser.readthedocs.io/en/latest/index.html

Then i try to customise the token create/login to change from username to email in the serializers.py

Original

class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
    required=False, style={'input_type': 'password'}
    )

    default_error_messages = {
        'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
        'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super(TokenCreateSerializer, self).__init__(*args, **kwargs)
    self.user = None
    self.fields[User.USERNAME_FIELD] = serializers.CharField(
        required=False
    )

def validate(self, attrs):
    self.user = authenticate(
        username=attrs.get(User.USERNAME_FIELD),
        password=attrs.get('password')
        )

        self._validate_user_exists(self.user)
        self._validate_user_is_active(self.user)
        return attrs

    def _validate_user_exists(self, user):
        if not user:
            self.fail('invalid_credentials')

    def _validate_user_is_active(self, user):
        if not user.is_active:
            self.fail('inactive_account')

Edited

class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
    required=False, style={'input_type': 'password'}
    )

    default_error_messages = {
        'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
        'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
    }

    def __init__(self, *args, **kwargs):
        super(TokenCreateSerializer, self).__init__(*args, **kwargs)
    self.user = None
    self.fields[User.EMAIL_FIELD] = serializers.EmailField(
        required=False
    )

def validate(self, attrs):
    self.user = authenticate(
        email=attrs.get(User.EMAIL_FIELD),
        password=attrs.get('password')
        )

        self._validate_user_exists(self.user)
        self._validate_user_is_active(self.user)
        return attrs

    def _validate_user_exists(self, user):
        if not user:
            self.fail('invalid_credentials')

    def _validate_user_is_active(self, user):
        if not user.is_active:
            self.fail('inactive_account')

but the result i get in the api is this

{
"non_field_errors": [
    "Unable to login with provided credentials."
]

I did try other method but all have same result. Is there a way to make it using of email to authenticate instead of username ?

like image 744
Jin Nii Sama Avatar asked Nov 29 '22 22:11

Jin Nii Sama


1 Answers

Djoser uses authenticate method from django.contrib.auth. By default the AUTHENTICATION_BACKENDS is set to usedjango.contrib.auth.backends.ModelBackend which will try to get the user model using default manager get_by_natural_key method, which does:

def get_by_natural_key(self, username):
    return self.get(**{self.model.USERNAME_FIELD: username})

You can check the source code for this.

My approach on this was to enhance django's UserManager, assuming your User model is subclass of AbstractUser, by trying to get the user by email or username, like this:

from django.contrib.auth.models import UserManager
from django.db.models import Q

class CustomUserManager(UserManager):

    def get_by_natural_key(self, username):
        return self.get(
            Q(**{self.model.USERNAME_FIELD: username}) |
            Q(**{self.model.EMAIL_FIELD: username})
        )

Use that in your User model

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    ...
    objects = CustomUserManager()

You should now be able to login using email or username

like image 129
Gabriel Muj Avatar answered Dec 04 '22 08:12

Gabriel Muj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!