Ricardo Carrera
Ricardo Carrera

Reputation: 133

DRF, allauth and dj-rest-auth - Authenticate with inactive users return None instead the user object

I'm using all-auth and dj-rest-auth to implement registration and login via email. Everything work fine but when making test, inactive users return invalid credentials message instead inactive account message. In the LoginSerializer, it seems that django.contrib.auth authenticate method doesn't return the user, but None. Here is the code:

settings.py

AUTHENTICATION_BACKENDS = [
   "django.contrib.auth.backends.AllowAllUsersModelBackend",
   "allauth.account.auth_backends.AuthenticationBackend"
]

REST_AUTH_REGISTER_SERIALIZERS = {
    'REGISTER_SERIALIZER': 'user.serializers.RegisterSerializer',
}

REST_AUTH_SERIALIZERS = {
    'LOGIN_SERIALIZER': 'user.serializers.LoginSerializer',
    'USER_DETAILS_SERIALIZER': 'user.serializers.UserDetailSerializer',
}

serializers.py

class LoginSerializer(serializers.Serializer):
    email = serializers.EmailField(required=True, allow_blank=False)
    password = serializers.CharField(style={'input_type': 'password'})

    def authenticate(self, **kwargs):
        return authenticate(self.context['request'], **kwargs)

    def _validate_email(self, email, password):
        user = None
        if email and password:
            user = self.authenticate(email=email, password=password)
        else:
            msg = _('Must include "email" and "password".')
            raise exceptions.ValidationError(msg)

        return user

    def validate(self, attrs):
        email = attrs.get('email')
        password = attrs.get('password')

        user = None
        if 'allauth' in settings.INSTALLED_APPS:
            from allauth.account import app_settings
            # Authentication through email
            if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
                user = self._validate_email(email, password)

        # Did we get back an inactive user?
        if user:
            if not user.is_active:
                msg = _('User account is disabled.')
                raise exceptions.ValidationError(msg)
        else:
            msg = _('Unable to log in with provided credentials.')
            raise exceptions.ValidationError(msg)

        # If required, is the email verified?
        if 'dj_rest_auth.registration' in settings.INSTALLED_APPS:
            from allauth.account import app_settings
            if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
                try:
                    email_address = user.emailaddress_set.get(email=user.email)
                except:
                    raise serializers.ValidationError(_('E-mail is not registered.'))
                else:
                    if not email_address.verified:
                        raise serializers.ValidationError(_('E-mail is not verified.'))


        attrs['user'] = user
        return attrs

tests.py

########################################################################
# LOG IN WITH INACTIVE USER
login_data = {
    'email': '[email protected]',
    'password': '9I8u7Y6t5R4e'
}
response = self.client.post('http://localhost:8000/api/auth/login/', login_data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
expected_error = {
    'non_field_errors': 'User account is disabled.'
}
response_error = {
    'non_field_errors': response.data['non_field_errors'][0]
}
self.assertEqual(response_error, expected_error)

Is there something that I missing?

Thanks in advance.

Upvotes: 0

Views: 1737

Answers (1)

Ricardo Carrera
Ricardo Carrera

Reputation: 133

In case anyone is interested, I found the problem: the allauth authentication backend override the django model backend. In order to resolve this, I create a class that inherit from allauth backend and add the function that allow all users to log in:

backend.py

from allauth.account.auth_backends import AuthenticationBackend

class AllowAllUsersModelBackend(AuthenticationBackend):

    def user_can_authenticate(self, user):
        return True

Then add it to settings:

settings.py

AUTHENTICATION_BACKENDS = [
   "user.backends.AllowAllUsersModelBackend",
]

Upvotes: 1

Related Questions