Praful Bagai
Praful Bagai

Reputation: 17392

Django - Login with Email

I want django to authenticate users via email, not via usernames. One way can be providing email value as username value, but I dont want that. Reason being, I've a url /profile/<username>/, hence I cannot have a url /profile/[email protected]/.

Another reason being that all emails are unique, but it happen sometimes that the username is already being taken. Hence I'm auto-creating the username as fullName_ID.

How can I just change let Django authenticate with email?

This is how I create a user.

username = `abcd28`
user_email = `[email protected]`
user = User.objects.create_user(username, user_email, user_pass)

This is how I login.

email = request.POST['email']
password = request.POST['password']
username = User.objects.get(email=email.lower()).username
user = authenticate(username=username, password=password)
login(request, user)

Is there any other of of login apart from getting the username first?

Upvotes: 138

Views: 140122

Answers (20)

Minilouze
Minilouze

Reputation: 55

While @mipadi's EmailBackend solution works, I would argue it's not the cleanest approach. It forces Django to treat an email as a username, leading to confusing error messages, logs, and potential issues with third-party apps expecting actual usernames. A cleaner solution would be to create a proper email-based authentication by subclassing AbstractBaseUser:

# yourapp/models.py
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.contrib.auth.base_user import BaseUserManager
from django.db import models

class UserManager(BaseUserManager):
    def create_user(self, email, password=None):
        user = self.model(email=self.normalize_email(email))
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password=None):
        user = self.create_user(email, password)
        user.is_staff = True
        user.is_superuser = True
        user.save()
        return user

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    
    objects = UserManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

Remember to set AUTH_USER_MODEL = 'yourapp.User' in settings.py.

Upvotes: 0

Joe Gasewicz
Joe Gasewicz

Reputation: 1485

This solution is for apps that have already many users & do not want to migrate them to a new user table.

Get the user, call check_password which returns True if the form password matches the hashed password stored in auth_user. Then all you need to do is call login(request, user). For example:

def login_user(request):
    if request.method == "GET":
        data = {
            "form": UserForm,
        }
        return render(request, "login.html", data)
    else:
        email = request.POST["email"]
        password = request.POST["password"]
        user = User.objects.get(email=email)
        if user:
            passwords_match = user.check_password(password)
            if passwords_match:
                login(request, user)
                logger.info(f"User with email {user.username} logged in")
                return redirect("feed")
        data = {
            "form": UserForm,
            "error": "Email & Password do not match"
        }
        return render(request, "login.html", data)

Then you need to alter the auth_user table's email column to include a unique constraint, I do this using a migration script:

# Generated by Django 5.0.6 on 2024-06-07 11:28

from django.db import migrations
from django.contrib.auth.models import User
from django.db import models


class Migration(migrations.Migration):

    dependencies = [
        ('user', '0002_email_from_username'),
    ]

    operations = [
        migrations.RunSQL("""
            ALTER TABLE auth_user
            ADD CONSTRAINT unique_email_uc UNIQUE (email);
        """)
    ]

Upvotes: 0

July, 2023 Update:

You can set up authentication with email and password instead of username and password and in this instruction, username is removed and I tried not to change the default Django settings as much as possible. *You can also see my answer explaining how to extend User model with OneToOneField() to add extra fields and you can see my answer and my answer explaining the difference between AbstractUser and AbstractBaseUser.

First, run the command below to create account app:

python manage.py startapp account

Then, set account app to INSTALLED_APPS and set AUTH_USER_MODEL = 'account.User' in settings.py as shown below:

# "settings.py"

INSTALLED_APPS = [
    ...
    "account", # Here
]

AUTH_USER_MODEL = 'account.User' # Here

Then, create managers.py just under account folder and create UserManager class extending (UM)UserManager in managers.py as shown below. *Just copy & paste the code below to managers.py and managers.py is necessary to make the command python manage.py createsuperuser work properly without any error:

# "account/managers.py"

from django.contrib.auth.models import UserManager as UM
from django.contrib.auth.hashers import make_password

class UserManager(UM):
    def _create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError("The given email must be set")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.password = make_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(email, password, **extra_fields)

Then, create User model extending AbstractUser and remove username by setting it None and set email with unique=True and set email to USERNAME_FIELD and set UserManager to objects in account/models.py as shown below. *Just copy & paste the code below to account/models.py:

# "account/models.py"

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractUser
from .managers import UserManager

class User(AbstractUser):
    username = None # Here
    email = models.EmailField(_("email address"), unique=True) # Here
    
    USERNAME_FIELD = 'email' # Here
    REQUIRED_FIELDS = []

    objects = UserManager() # Here

Or, you can also create User model extending AbstractBaseUser and PermissionsMixin as shown below. *This code below with AbstractBaseUser and PermissionsMixin is equivalent to the code above with AbstractUser:

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils import timezone
from .managers import UserManager

class User(AbstractBaseUser, PermissionsMixin):
    first_name = models.CharField(_("first name"), max_length=150, blank=True)
    last_name = models.CharField(_("last name"), max_length=150, blank=True)
    email = models.EmailField(_("email address"), unique=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )
    is_active = models.BooleanField(
        _("active"),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as active. "
            "Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(_("date joined"), default=timezone.now)

    USERNAME_FIELD = 'email'

    objects = UserManager() # Here

    class Meta:
        verbose_name = _("user")
        verbose_name_plural = _("users")

*Don't extend DefaultUser(User) model as shown below otherwise there is error:

# "account/models.py"

from django.contrib.auth.models import User as DefaultUser

class User(DefaultUser):
    ...

Then, create UserAdmin class extending UA(UserAdmin) in account/admin.py as shown below. *Just copy & paste the code below to account/admin.py:

from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.admin import UserAdmin as UA
from .models import User

@admin.register(User)
class UserAdmin(UA):
    fieldsets = (
        (None, {"fields": ("password",)}),
        (_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
        (
            _("Permissions"),
            {
                "fields": (
                    "is_active",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                ),
            },
        ),
        (_("Important dates"), {"fields": ("last_login", "date_joined")}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": ("email", "password1", "password2"),
            },
        ),
    )
    list_display = ("email", "first_name", "last_name", "is_staff")
    ordering = ("-is_staff",)
    readonly_fields=('last_login', 'date_joined')

Then, run the command below. *This must be the 1st migration to database when customizing User model in this way otherwise there is error according to my experiments and the doc so before you develop your Django project, you must first create custom User model:

python manage.py makemigrations && python manage.py migrate

Then, run the command below:

python manage.py createsuperuser

Then, run the command below:

python manage.py runserver 0.0.0.0:8000

Then, open the url below:

http://localhost:8000/admin/login/

Finally, you can log in with email and password as shown below:

enter image description here

And, this is Add custom user page as shown below:

enter image description here

Upvotes: 4

user2023630
user2023630

Reputation: 59

All of these are horrendously complicated for what should be a simple problem.

Check that a user exists with that email, then get that user's username for the argument in Django's authenticate() method.

try:
    user = User.objects.get(email = request_dict['email'])
    user = authenticate(username = user.username, password = request_dict['password'])
except:
    return HttpResponse('User not found.', status = 400)

Upvotes: 1

bob
bob

Reputation: 751

Django 4.0

There are two main ways you can implement email authentication, taking note of the following:

  • emails should not be unique on a user model to mitigate misspellings and malicious use.
  • emails should only be used for authentication if they are verified (as in we have sent a verification email and they have clicked the verify link).
  • We should only send emails to verified email addresses.

Custom User Model

A custom user model is recommended when starting a new project as changing mid project can be tricky.

We will add an email_verified field to restrict email authentication to users with a verified email address.

# app.models.py
from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    email_verified = models.BooleanField(default=False)

We will then create a custom authentication backend that will substitute a given email address for a username.

This backend will work with authentication forms that explicitly set an email field as well as those setting a username field.

# app.backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

UserModel = get_user_model()


class CustomUserModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.get(
                Q(username__exact=username) | (Q(email__iexact=username) & Q(email_verified=True))
            )
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

We then modify our projects settings.py to use our custom user model and authentication backend.

# project.settings.py

AUTH_USER_MODEL = "app.User"
AUTHENTICATION_BACKENDS = ["app.backends.CustomUserModelBackend"]

Be sure that you run manage.py makemigrations before you migrate and that the first migration contains these settings.

Extended User Model

While less performant than a custom User model (requires a secondary query), it may be better to extend the existing User model in an existing project and may be preferred depending on login flow and verification process.

We create a one-to-one relation from EmailVerification to whichever User model our project is using through the AUTH_USER_MODEL setting.

# app.models.py
from django.conf import settings
from django.db import models


class EmailVerification(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_query_name="verification"
    )
    verified = models.BooleanField(default=False)

We can also create a custom admin that includes our extension inline.

# app.admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from .models import EmailVerification

UserModel = get_user_model()


class VerificationInline(admin.StackedInline):
    model = EmailVerification
    can_delete = False
    verbose_name_plural = 'verification'


class UserAdmin(BaseUserAdmin):
    inlines = (VerificationInline,)


admin.site.unregister(UserModel)
admin.site.register(UserModel, UserAdmin)

We then create a backend similar to the one above that simply checks the related models verified field.

# app.backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

UserModel = get_user_model()


class ExtendedUserModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.get(
                Q(username__exact=username) | (Q(email__iexact=username) & Q(verification__verified=True))
            )
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

We then modify our projects settings.py to use our authentication backend.

# project.settings.py

AUTHENTICATION_BACKENDS = ["app.backends.ExtendedUserModelBackend"]

You can then makemigrations and migrate to add functionality to an existing project.

Notes

  • if usernames are case insensitive change Q(username__exact=username) to Q(username__iexact=username).
  • In production prevent a new user registering with an existing verified email address.

Upvotes: 30

Ronald Ngwenya
Ronald Ngwenya

Reputation: 17

The default user model inherits/ Extends an Abstract class. The framework should be lenient to a certain amount of changes or alterations.

A simpler hack is to do the following: This is in a virtual environment

  1. Go to your django installation location and find the Lib folder
  2. navigate to django/contrib/auth/
  3. find and open the models.py file. Find the AbstractUser class line 315

LINE 336 on the email attribute add unique and set it to true

email = models.EmailField(_('email address'), blank=True,unique=True)

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
  1. Done, makemigrations & migrate

Do this at you own risk,

Upvotes: -1

bitFez
bitFez

Reputation: 332

It seems that the method of doing this has been updated with Django 3.0.

A working method for me has been:

authentication.py # <-- I placed this in an app (did not work in the project folder alongside settings.py

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User

class EmailBackend(BaseBackend):
    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

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

Then added this to the settings.py file

AUTHENTICATION_BACKENDS = (
    'appname.authentication.EmailBackend',
)

Upvotes: 5

Casman Ridder
Casman Ridder

Reputation: 27

Pretty simple. There is no need for any additional classes.

When you create and update a user with an email, just set the username field with the email.

That way when you authenticate the username field will be the same value of the email.

The code:

# Create
User.objects.create_user(username=post_data['email'] etc...)

# Update
user.username = post_data['email']
user.save()

# When you authenticate
user = authenticate(username=post_data['email'], password=password)

Upvotes: -1

mipadi
mipadi

Reputation: 411132

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

Upvotes: 152

Wariored
Wariored

Reputation: 1343

I have created a helper for that: function authenticate_user(email, password).

from django.contrib.auth.models import User


def authenticate_user(email, password):
    try:
        user = User.objects.get(email=email)
    except User.DoesNotExist:
        return None
    else:
        if user.check_password(password):
            return user

    return None

class LoginView(View):
    template_name = 'myapp/login.html'

    def get(self, request):
        return render(request, self.template_name)

    def post(self, request):
        email = request.POST['email']
        password = request.POST['password']
        user = authenticate_user(email, password)
        context = {}

        if user is not None:
            if user.is_active:
                login(request, user)

                return redirect(self.request.GET.get('next', '/'))
            else:
                context['error_message'] = "user is not active"
        else:
            context['error_message'] = "email or password not correct"

        return render(request, self.template_name, context)

Upvotes: 4

Nicholas
Nicholas

Reputation: 951

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.

Upvotes: 94

sagar rathod
sagar rathod

Reputation: 1

If You created Custom database, from there if you want to validate your email id and password.

  1. Fetch the Email id and Password with models.objects.value_list('db_columnname').filter(db_emailname=textbox email)

2.assign in list fetched object_query_list

3.Convert List to String

Ex :

  1. Take the Html Email_id and Password Values in Views.py

    u_email = request.POST.get('uemail')

    u_pass = request.POST.get('upass')

  2. Fetch the Email id and password from the database

    Email = B_Reg.objects.values_list('B_Email',flat=True).filter(B_Email=u_email)

    Password = B_Reg.objects.values_list('Password',flat=True).filter(B_Email=u_email)

  3. Take the Email id and password values in the list from the Query value set

    Email_Value = Email[0]

    Password_Value=Password[0]

  4. Convert list to String

    string_email = ''.join(map(str, Email_Value))

    string_password = ''.join(map(str, Password_Value))

Finally your Login Condition

if (string_email==u_email and string_password ==u_pass)

Upvotes: 0

Himanshu Singh
Himanshu Singh

Reputation: 755

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',)

Upvotes: 38

Shakil Ahmmed
Shakil Ahmmed

Reputation: 128

Authentication with Email For Django 2.x

def admin_login(request):
if request.method == "POST":
    email = request.POST.get('email', None)
    password = request.POST.get('password', None)
    try:
        get_user_name = CustomUser.objects.get(email=email)
        user_logged_in =authenticate(username=get_user_name,password=password)
        if user_logged_in is not None:
            login(request, user_logged_in)
            messages.success(request, f"WelcomeBack{user_logged_in.username}")
            return HttpResponseRedirect(reverse('backend'))
        else:
            messages.error(request, 'Invalid Credentials')
            return HttpResponseRedirect(reverse('admin_login'))
    except:
        messages.warning(request, 'Wrong Email')
        return HttpResponseRedirect(reverse('admin_login'))

else:
    if request.user.is_authenticated:
        return HttpResponseRedirect(reverse('backend'))
    return render(request, 'login_panel/login.html')

Upvotes: 0

Chetan
Chetan

Reputation: 31

Authentication with Email and Username For Django 2.x

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class EmailorUsernameModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

In settings.py, add following line,

AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']

Upvotes: 3

Gabriel Garcia
Gabriel Garcia

Reputation: 702

Email and Username Authentication for Django 2.X

Having in mind that this is a common question, here's a custom implementation mimicking the Django source code but that authenticates the user with either username or email, case-insensitively, keeping the timing attack protection and not authenticating inactive users.

from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q

class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
        except UserModel.DoesNotExist:
            UserModel().set_password(password)
        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

Always remember to add it your settings.py the correct Authentication Backend.

Upvotes: 6

For Django 2

username = get_object_or_404(User, email=data["email"]).username
        user = authenticate(
            request, 
            username = username, 
            password = data["password"]
        )
        login(request, user)

Upvotes: 0

Animesh Timsina
Animesh Timsina

Reputation: 436

from django.contrib.auth.models import User

from django.db import Q

class EmailAuthenticate(object):

    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(email=username) | Q(username=username))
        except User.DoesNotExist:
            return None
        except MultipleObjectsReturned:
            return User.objects.filter(email=username).order_by('id').first()

        if user.check_password(password):
            return user
        return None

    def get_user(self,user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

And then in settings.py:

AUTHENTICATION_BACKENDS = (
  'articles.backends.EmailAuthenticate',
)

where articles is my django-app, backends.py is the python file inside my app and EmailAuthenticate is the authentication backend class inside my backends.py file

Upvotes: 1

anuragb26
anuragb26

Reputation: 1475

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

Upvotes: 14

Hung Nguyen
Hung Nguyen

Reputation: 1381

You should customize ModelBackend class. My simple code:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

class YourBackend(ModelBackend):

  def authenticate(self, username=None, password=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        if '@' in username:
            UserModel.USERNAME_FIELD = 'email'
        else:
            UserModel.USERNAME_FIELD = 'username'

        user = UserModel._default_manager.get_by_natural_key(username)
    except UserModel.DoesNotExist:
        UserModel().set_password(password)
    else:
        if user.check_password(password) and self.user_can_authenticate(user):
            return user

And in settings.py file, add:

AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']

Upvotes: 2

Related Questions