I am trying to create an auth backend to allow my users to log in using either their email address or their username in Django 1.6 with a custom user model. The backend works when I log in with a user name but for some reason does not with an email. Is there something I am forgetting to do?
from django.conf import settings from django.contrib.auth.models import User class EmailOrUsernameModelBackend(object): """ This is a ModelBacked that allows authentication with either a username or an email address. """ def authenticate(self, username=None, password=None): if '@' in username: kwargs = {'email': username} else: kwargs = {'username': username} try: user = User.objects.get(**kwargs) if user.check_password(password): return user except User.DoesNotExist: return None def get_user(self, username): try: return User.objects.get(pk=username) except User.DoesNotExist: return None
Edit: As suggested I have inherited from ModelBackend and installed it in my settings In my settings I have this AUTHENTICATION_BACKENDS = ( 'users.backends', 'django.contrib.auth.backends.ModelBackend', ) And I have changed the backend to this:
from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.backends import ModelBackend class EmailOrUsernameModelBackend(ModelBackend): """ This is a ModelBacked that allows authentication with either a username or an email address. """ def authenticate(self, username=None, password=None): if '@' in username: kwargs = {'email': username} else: kwargs = {'username': username} try: user = User.objects.get(**kwargs) if user.check_password(password): return user except User.DoesNotExist: return None def get_user(self, username): try: return User.objects.get(pk=username) except User.DoesNotExist: return None
Now I get an Module "users" does not define a "backends" attribute/class
error.
Email authentication for Django 3.x For using email/username and password for authentication instead of the default username and password authentication, we need to override two methods of ModelBackend class: authenticate() and get_user():
get_username() will return a string of the users email. request. user. username will return a method.
auth import authenticate, login def my_view(request): username = request. POST['username'] password = request. POST['password'] user = authenticate(username=username, password=password) if user is not None: if user. is_active: login(request, user) # Redirect to a success page.
Yet another solution:
from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from django.db.models import Q class EmailOrUsernameModelBackend(ModelBackend): """ Authentication backend which allows users to authenticate using either their username or email address Source: https://stackoverflow.com/a/35836674/59984 """ def authenticate(self, request, username=None, password=None, **kwargs): # n.b. Django <2.1 does not pass the `request` user_model = get_user_model() if username is None: username = kwargs.get(user_model.USERNAME_FIELD) # The `username` field is allows to contain `@` characters so # technically a given email address could be present in either field, # possibly even for different users, so we'll query for all matching # records and test each one. users = user_model._default_manager.filter( Q(**{user_model.USERNAME_FIELD: username}) | Q(email__iexact=username) ) # Test whether any matched user has the provided password: for user in users: if user.check_password(password): return user if not users: # Run the default password hasher once to reduce the timing # difference between an existing and a non-existing user (see # https://code.djangoproject.com/ticket/20760) user_model().set_password(password)
Fixes:
@
is not prohibited in the username field, so unless custom User model prohibits @
symbol, it can't be used to distinguish between username and email.MultipleObjectsReturned
exception if UserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username))
is used.except:
is generally bad practiceDownside - if there are two users, using the same email, one in the username, the other in email, and they have the same password, then it's prone to authenticating the first match. I guess the chances of this is highly unlikely.
Also note: any of the approaches should enforce unique email
field in the User model, since the default User model does not define unique email, which would lead to either unhandled exception in case User.objects.get(email__iexact="...")
is used, or authenticating the first match. In any case, using email to login assumes that email is unique.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With