Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Log in user using either email address or username in Django

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.

like image 791
user3282276 Avatar asked Aug 14 '14 20:08

user3282276


People also ask

How do I authenticate using email and password in Django?

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():

How can I see the username as logged in Django?

get_username() will return a string of the users email. request. user. username will return a method.

How do I authenticate username and password in Django?

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.


1 Answers

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:

  • By default, @ 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.
  • Technically, there can be two users using the same email, one in the email field, the other in the username. Unless such possibility is restricted, it can lead to either user not being able to authenticate, or unhandled MultipleObjectsReturned exception if UserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username)) is used.
  • Catching any exception with except: is generally bad practice

Downside - 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.

like image 111
1bit0fMe Avatar answered Sep 18 '22 07:09

1bit0fMe