Omnia Osman
Omnia Osman

Reputation: 118

Django authenticate function return None although the User isn't None

I am trying to make login with a custom User which using email instead of username, but authenticate(email=email, password=password) returns None although the user is exist

class CustomUserManager(UserManager):
    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', 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)


class User(AbstractUser):
    email    = models.EmailField(max_length=254, unique=True)
    username = None
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = [] 
    objects = CustomUserManager() # use the custom manager
    
    def __str__(self):
        return self.email

Views.py:

class SignUp(CreateView):
    form_class    = SignUpForm
    template_name = 'signup.html' 
    
    def form_valid(self, form):
        email     = form.cleaned_data['email']
        password1 = make_password(form.cleaned_data['password1'])
        password2 = make_password(form.cleaned_data['password2'])
        user      = User(email=email, password=password1, username=email)
        user      = user.save()
        print("User created!")
        user      = authenticate(username=email, password=password1)
        login(self.request, user)
        return redirect('home')



class LoginView(FormView):
    template_name = 'login.html'
    form_class    =  LoginForm 

    def form_valid(self, form):
        email    = form.cleaned_data['email']
        password = make_password(form.cleaned_data['password'])
        print(email, password) # for debugging purposes
        user     = authenticate(username=email, password=password)
        print(user) # for debugging purposes
        if user is not None:
            print('do')
            login(self.request, user)
            return redirect('home')
        else:
            messages.info(self.request, 'invalid username or password')
            return redirect('login')

backends.py:

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


class EmailBackend(ModelBackend):
    def authenticate(self, request, password=None, email=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=email)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

settings.py

AUTH_USER_MODEL = 'accounts.User'

AUTHENTICATION_BACKENDS = [

'accounts.backends.EmailBackend',  

]

forms.py:

from .models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms


class SignUpForm(UserCreationForm): 
    email     = forms.EmailField(widget=forms.EmailInput(
        attrs={'class'      : 'form-control', 
               'type'       : "text",
               'class'      : "form-control",
               'placeholder': "Email"}))
    password1 = forms.CharField(min_length=8, label='Password', widget=forms.PasswordInput(
        attrs={'id'         : "password-field",
               'class'      : 'form-control', 
               'type'       : "password",
               'class'      : "form-control",
               'placeholder': "Password",
               'toggle'     : "#password-field",}))
    password2 = forms.CharField(min_length=8, label='Confirm Password', 
                               widget=forms.PasswordInput(
        attrs={'id'         :"password-field",
               'class'      : 'form-control', 
               'type'       : "password",
               'class'      : "form-control",
               'placeholder': "Confirm Password",}))
    class Meta:
        model  = User
        fields = ('email', 'password1', 'password2')


class LoginForm(forms.Form):
    email     = forms.CharField(widget=forms.EmailInput(
        attrs={'class'      : 'form-control', 
               'type'       : "text",
               'class'      : "form-control",
               'placeholder': "Email"}))
    password  = forms.CharField(min_length=8, label='Password', widget=forms.PasswordInput(
        attrs={'id'         : "password-field",
               'class'      : 'form-control', 
               'type'       : "password",
               'class'      : "form-control",
               'placeholder': "Password",
               'toggle'     : "#password-field",}))
    class Meta:
        model = User
        fields = ('email', 'password',)

I want to know why the value of user is None. I am sure the email and password matched with database.

Upvotes: 0

Views: 59

Answers (1)

Tarquinius
Tarquinius

Reputation: 1948

The problem is your usage of make_password and check_password.

The LoginView should not do make_password. This is the function that translates the raw password to the encrypted password. Since you want to store only the encrypted password in your database, you want to use this function when registering a new user but not when logging one in.

The function of check_password takes two required arguments. This is an error in your code which is not yet encountered.

Your LoginView should pass the raw password to your authenticate method (right now you are passing the encrypted one). Then in your authenticate method you should check the raw password versus the encrypted which comes from the database. These are the two arguments you need to pass to check_password. If you pass the encrypted password to the check_password method it will encrypt the encrypted password another time. You will see that check_password("xyz", "xyz") is not True.

class LoginView(FormView):
    template_name = 'login.html'
    form_class    =  LoginForm 

    def form_valid(self, form):
        email    = form.cleaned_data['email']
        password = form.cleaned_data['password']  # pass the raw one
        print(email, password) # for debugging purposes
        user     = authenticate(email=email, password=password) # change username to email here
        print(user) # for debugging purposes
        if user is not None:
            print('do')
            login(self.request, user)
            return redirect('home')
        else:
            messages.info(self.request, 'invalid username or password')
            return redirect('login')
class EmailBackend(ModelBackend):
    def authenticate(self, request, password=None, email=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=email)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password, user.password):  # add pw field to compare
                return user
        return None

Upvotes: 1

Related Questions