I have a LoginSerializer that has the block of code as below
def validate(self, attrs):
username = attrs.get('username', '')
password = attrs.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
if user.is_active is False:
raise AuthenticationFailed(
'Account is disabled, contact admin')
if not user.is_verified:
raise AuthenticationFailed('Email is not verified')
return {
'username': user.username,
'firstname': user.firstname,
'lastname': user.lastname,
'role': user.role,
'tokens': user.tokens
}
else:
raise AuthenticationFailed('Invalid credentials, try again')
and a test case as below;
class UserLoginTest(BaseTest):
def test_inactive_user_can_login(self):
self.client.post(
self.register_public, data=valid_user, format='json')
user = User.objects.get(username=valid_user['username'])
user.is_verified = True
user.is_active = False
user.save()
response = self.client.post(
self.login_url, valid_login_user, format='json')
print(response.data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
When I run the test with is_active = False I get Invalid credentials, try again. Why is it that when is_active=False the user is not found even though the user is there? Same with when I try to login from swagger.
EDIT
I have read that I can use
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.AllowAllUsersModelBackend']
then I will be able to check for is_active manually otherwise django handles that and returns a None. What are the dangers of doing this?
This happens because you are using .authenticate()
which by default goes through all backends listed in AUTHENTICATION_BACKENDS
. If not listed in settings, django.contrib.auth.backends.ModelBackend
is used, this backend verifies .is_active()
field:
...
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, "is_active", None)
return is_active or is_active is None
...
That snippet runs before your verification and returns None
to user
variable. So, it fails on if user
condition (user is None) thus raise AuthenticationFailed('Invalid credentials, try again')
About AllowAllUsersModelBackend
the only thing it does is override this method to allow inactive users to login:
class AllowAllUsersModelBackend(ModelBackend):
def user_can_authenticate(self, user):
return True
The only risk i can see, is using this backend and not checking .is_active()
field manually. Unless if it is intended that inactive users can login into your system.