Django - Login with Email
Solution 1:
You should write a custom authentication backend. Something like this will work:
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Then, set that backend as your auth backend in your settings:
AUTHENTICATION_BACKENDS = ['path.to.auth.module.EmailBackend']
Updated. Inherit from ModelBackend
as it implements methods like get_user()
already.
See docs here: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend
Solution 2:
If you’re starting a new project, django highly recommended you to set up a custom user model. (see https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project)
and if you did it, add three lines to your user model:
class MyUser(AbstractUser):
USERNAME_FIELD = 'email'
email = models.EmailField(_('email address'), unique=True) # changes email to unique and blank to false
REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS
Then authenticate(email=email, password=password)
works, while authenticate(username=username, password=password)
stops working.
Solution 3:
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():
The get_user method takes a user_id – which could be a username, database ID or whatever, but has to be unique to your user object – and returns a user object or None. If you have not kept email as a unique key, you will have to take care of multiple result returned for the query_set. In the below code, this has been taken care of by returning the first user from the returned list.
from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try: #to allow authentication through phone number or any other field, modify the below statement
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
UserModel().set_password(password)
except MultipleObjectsReturned:
return User.objects.filter(email=username).order_by('id').first()
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
def get_user(self, user_id):
try:
user = UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
By default, AUTHENTICATION_BACKENDS is set to:
['django.contrib.auth.backends.ModelBackend']
In settings.py file, add following at the bottom to override the default:
AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)
Solution 4:
I had a similar requirement where either username/email should work for the username field.In case someone is looking for the authentication backend way of doing this,check out the following working code.You can change the queryset if you desire only the email.
from django.contrib.auth import get_user_model # gets the user_model django default or your own custom
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
# Class to permit the athentication using email or username
class CustomBackend(ModelBackend): # requires to define two functions authenticate and get_user
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
# below line gives query set,you can change the queryset as per your requirement
user = UserModel.objects.filter(
Q(username__iexact=username) |
Q(email__iexact=username)
).distinct()
except UserModel.DoesNotExist:
return None
if user.exists():
''' get the user object from the underlying query set,
there will only be one object since username and email
should be unique fields in your models.'''
user_obj = user.first()
if user_obj.check_password(password):
return user_obj
return None
else:
return None
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
Also add AUTHENTICATION_BACKENDS = ( 'path.to.CustomBackend', ) in settings.py