Reputation: 118
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
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