I am writing a custom auth backend for a custom user model inherited from AbstractUser in django. I have gone through default auth backend of django and saw that user object is fetched using username with UserModel._default_manager.get_by_natural_key(username)
authenticate() is as below
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
I have already written a custom aut backend, but I am asking this out of curiosity, Why is there a check on password upon a DoesNotExist exception?
I have tried ipdb
in authenticate method and verified that upon getting username of a non-existing user, control flow goes to except block. And a UserModel().set_password(password)
was issued in terminal. Being not sure what happens in the background, I inspected code of set_password()
of auth.models
. But it was just raising a NotImplementedError
exception. I am not sure how this helps.
Also how to raise a UserDoesNotExist or django.core.exceptions.ObjectDoesNotExist exception correctly
upon a failed object fetch operation on User Model in a custom auth backend? Idea is to stop the execution and present the user with the right feedback message instead of raising an exception and trying out next authentication backend as given in AUTHENTICATION_BACKENDS() in settings.py.
TIA
The comment explains exactly why. If it immediately returned False, it would take much less time than for a user that did not exist. An attacker would then be able to tell the difference between an existing and a non-existing username.