Django Authenticate Backends with Email - django

I want to login with email not username like this, please help
class loginUser(View):
def get(self, request):
lF = loginForm
return render(request, 'UserMember/login.html', {'lF': lF})
def post(self, request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return render(request, 'UserMember/private.html')
else:
return HttpResponse('login fail')

First thing is to create a default email field.
# models.py
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
# settings.py (remember to migrate)
AUTH_USER_MODEL = 'accounts.CustomUser' # new
Next, create your custom email backend:
# backends.py (in-app)
class EmailBackend(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)
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
# settings.py (migrate again)
AUTH_USER_MODEL = 'accounts.CustomUser'
AUTHENTICATION_BACKENDS = ['accounts.backends.EmailBackend'] # new
If you plan on using Django's default register/login forms, do:
# form.py
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import get_user_model
from django import forms
class RegisterForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('email', 'username', 'password1', 'password2')
class LoginForm(AuthenticationForm):
username = forms.CharField(label='Email / Username')
And then it's only the views and URLs to handle.
Ref

you need to work with allAuth
Follow this Link
hope it will be helpful.

Related

Cant login using hashed password in django

I am trying to implement a custom user model. Whenever I try to log in for a user (created using the registration form), it returns no user.
but if I create a user using the admin panel then the login function works perfectly.
I think the problem is with password hashing. Tried some solve from here and there but seems like I can't find what I am looking for.
The problem I am having :
email: yeamin21#outlook.com
pass: 1234 works (created using admin panel)
but, email: yeamin21#outlook.com
pass: hashed(1234) does not (created using the registration form)
models.py
from django.contrib.auth.base_user import AbstractBaseUser
from django.db import models
class User(AbstractBaseUser):
username = models.CharField(max_length=30,unique=True)
email = models.EmailField(verbose_name='Email',unique=True)
name = models.CharField(max_length=100)
is_active = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
is_restaurant = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
def __str__(self):
return self.name
class Restaurant(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
location = models.CharField(max_length=200)
def __str__(self):
return self.user.email
forms.py
from django.contrib.auth.forms import UserCreationForm
from django.db import transaction
from Customer.models import User, Restaurant
class RestaurantSignUpForm(UserCreationForm):
class Meta(UserCreationForm):
model = User
fields = ['email','name','username']
#transaction.atomic
def save(self, commit=True):
user = super().save(commit=False)
user.is_restaurant=True
user.is_active=True
user.save()
restaurant = Restaurant.objects.create(user=user)
restaurant.save()
return user
backend.py
from django.contrib.auth.backends import BaseBackend, ModelBackend
from Customer.models import User
class CustomBackend(ModelBackend):
def authenticate(self,email=None,password=None):
try:
user=User.objects.get(email=email)
print('active')
if user.check_password(password) is True:
return user
except User.DoesNotExist:
return None
return User.objects.get(email=User.email)
def get_user(self, email):
try:
return User.objects.get(email)
except User.DoesNotExist:
return None
views.py
from django.contrib.auth import login, authenticate
from django.http import HttpResponse
from django.shortcuts import render
from django.views import generic
from Customer.forms import RestaurantSignUpForm
from Customer.models import User
def login_page(request):
if request.method == 'POST':
email = request.POST['email']
password = request.POST['password']
user = authenticate(request, email=email, password=password)
print(user)
if user is not None:
login(request, user)
return HttpResponse("Logged in")
else:
print(user)
context = {}
return render(request, 'login.html', context)
The problem is fixed
thanks to this https://stackoverflow.com/a/48971226/10602634
def authenticate(self, request, email=None, password=None):
try:
user= User.objects.get(email=email)
if check_password(password, user.password):
return user
except User.DoesNotExist:
return None

Custom Authentication in django is not working

I am new to django and I wanted to authenticate user on email or username with password hence I wrote a custom authentication as shown in documentation but it doesn't seem to be called and I have no idea what do I do?
settings.py
AUTHENTICATION_BACKENDS = ('accounts.backend.AuthBackend',)
views.py
def login(request):
if request.method == 'POST':
username_or_email = request.POST['username']
password = request.POST['password']
user = authenticate(username=username_or_email, password=password)
print(user)
if user is not None:
return reverse('task:home')
else:
messages.error(request, "Username or password is invalid")
return render(request, 'accounts/login.html')
else:
return render(request, 'accounts/login.html')
backend.py
from django.contrib.auth.models import User
from django.db.models import Q
class AuthBackend(object):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
def authenticate(self, username, password):
print('inside custom auth')
try:
user = User.objects.get(
Q(username=username) | Q(email=username) )
print(user)
except User.DoesNotExist:
return None
print(user)
if user.check_password(password):
return user
else:
return None
I wrote this print statements in my class to check if they are being called and being written in console. However, they are not being printed and the print statement in views.py prints None
You need to extend the ModelBackend from django.contrib.auth.backends
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
User = get_user_model()
class AuthBackend(ModelBackend):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
def authenticate(self, request, username=None, password=None):
print('inside custom auth')
try:
user = User.objects.get(
Q(username=username) | Q(email=username) )
print(user)
except User.DoesNotExist:
return None
print(user)
if user.check_password(password):
return user
else:
return None
And also in settings.py don't forget to add your custom backend authentication
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'accounts.backend.AuthBackend'
]
Another Possible Solution
From you code what I am seeing is that you want your email should treat as user_name of User model. You can easily modify Django's AbstructUser model like following
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# your necessary additional fields
USERNAME_FIELD = 'email' # add this line
Now email field will treat as an user_name field. No need to add custom authentication-backend
import sys
from django.contrib.auth import get_user_model
from accounts.models import Token
User = get_user_model()
class PasswordlessAuthenticationBackend:
def authenticate(self, uid=None):
print('uuuuuuuuu')
print('uid', uid, file=sys.stderr)
if not Token.objects.filter(uid=uid).exists():
print('no token found', file=sys.stderr)
return None
token = Token.objects.get(uid=uid)
print('got token', file=sys.stderr)
try:
user = User.objects.get(email=token.email)
print('got user', file=sys.stderr)
return user
except User.DoesNotExist:
print('new user', file=sys.stderr)
return User.objects.create(email=token.email)
def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None

Registration form django that inherits form UserCreationForm

I am following a tutorial that has created the following registration form:
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required = True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password1',
'password2'
)
def save(self, commit = True):
user = super(RegistrationForm, self).save(commit= False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return User
Why isemail = forms.EmailField(required = True) the only field mentioned outside of class Meta, what is the purpose of this?
email field on the contrib.auth.AbstractUser (which is subclassed by User) has:
email = models.EmailField(_('email address'), blank=True)
which means that it is allowed to be blank.
Because we want it to be required in the form (for the purposes of the tutorial I assume), we must declare it explicitly.
If you want to create user registration system in django you can create forms.py file paste within it :
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
class RegisterUserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'input'}))
password2 = forms.CharField(label="Repeat password", widget=forms.PasswordInput(attrs={'class': 'input'}))
class Meta:
model = User
fields = ['username', 'email']
widgets = {
'username': forms.TextInput(attrs={'class': 'input'}),
'email': forms.EmailInput(attrs={'class': 'input'})
}
# Validating password
def clean_password2(self):
cd = self.cleaned_data
if cd['password2'] != cd['password']:
raise ValidationError("Password don't match")
return cd['password2']
And in views.py
-*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import render
# Create your views here.
from django.views.generic import CreateView
from account.forms import RegisterUserForm
class RegisterUserView(CreateView):
form_class = RegisterUserForm
template_name = "account/register.html"
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return HttpResponseForbidden()
return super(RegisterUserView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.save()
return HttpResponse('User registered')
We override the dispath() method to make sure that the user can access the form if and only if he's not authenticated .
And for form_valid method we encrypt the password using set_password() method and then we commit to the database.
You probably will redirect the user if success rather than returning HttpResponse() as i did .
Because the default UserCreationForm doesn't have the EmailField which represents the email. But it has the other fields and there's no need to add them.
If you added a special field that is not included in the UserCreationForm like the EmailField you have to add it there.

Django form meta and validation

I have form:
class FindAdvert(forms.ModelForm):
class Meta:
model = Advert
fields = ('id', 'code')
But in this way I can't 'login' because validation return error: id already exist.
How modify this form to allow 'login' (instead of registration)?
You don't need ModelForm for login, because it's semantical goal - create|edit database data (anyway you can hardcode id in your view - but it is wrong way).
So create simple Form with custom validation rules (in clean method or in your views), something like that below:
class LoginForm(forms.Form):
username = forms.CharField(
label=u'Username',
required=True,
)
password = forms.CharField(
label=u'Password',
required=True,
widget=forms.PasswordInput
)
def clean(self):
cleaned_data = super(LoginForm, self).clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
if (username and password and User.objects.filter(username=username).count() == 0)\
or (username and password and User.objects.filter(username=username).count() == 1 and
User.objects.get(username=username).password != password):
raise forms.ValidationError(u'Wrong username or password')
return cleaned_data
views:
from django.contrib.auth import logout, authenticate, login
from django.views.generic import FormView, RedirectView
# ...
class LoginFormView(FormView):
template_name = 'common/login.html'
form_class = LoginForm
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
logout(request)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None and user.is_superuser:
login(request, user)
return self.form_valid(form)
else:
return self.form_invalid(form)
def get_success_url(self):
return self.request.GET.get('next') or reverse('human:add')
class LogoutRedirectView(RedirectView):
permanent = False
def get_redirect_url(self, *args, **kwargs):
logout(self.request)
return reverse('common:login')

Custom login in Django

Django newbie here.
I wrote simplified login form which takes email and password. It works great if both email and password are supplied, but if either is missing i get KeyError exception. According to django documentation this should never happen:
By default, each Field class assumes the value is required, so if you pass an empty value -- either None or the empty string ("") -- then clean() will raise a ValidationError exception
I tried to write my own validators for fields (clean_email and clean_password), but it doesn't work (ie I get KeyError exception). What am I doing wrong?
class LoginForm(forms.Form):
email = forms.EmailField(label=_(u'Your email'))
password = forms.CharField(widget=forms.PasswordInput, label=_(u'Password'))
def clean_email(self):
data = self.cleaned_data['email']
if not data:
raise forms.ValidationError(_("Please enter email"))
return data
def clean_password(self):
data = self.cleaned_data['password']
if not data:
raise forms.ValidationError(_("Please enter your password"))
return data
def clean(self):
try:
username = User.objects.get(email__iexact=self.cleaned_data['email']).username
except User.DoesNotExist:
raise forms.ValidationError(_("No such email registered"))
password = self.cleaned_data['password']
self.user = auth.authenticate(username=username, password=password)
if self.user is None or not self.user.is_active:
raise forms.ValidationError(_("Email or password is incorrect"))
return self.cleaned_data
You could leverage Django's built-in way to override how Authentication happens by setting
AUTHENTICATION_BACKENDS in your settings.py
Here's my EmailAuthBackend:
#settings.py
AUTHENTICATION_BACKENDS = (
'auth_backend.auth_email_backend.EmailBackend',
'django.contrib.auth.backends.ModelBackend',
)
#auth_email_backend.py
from django.contrib.auth.backends import ModelBackend
from django.forms.fields import email_re
from django.contrib.auth.models import User
class EmailBackend(ModelBackend):
"""
Authenticate against django.contrib.auth.models.User
"""
def authenticate(self, **credentials):
return 'username' in credentials and \
self.authenticate_by_username_or_email(**credentials)
def authenticate_by_username_or_email(self, username=None, password=None):
try:
user = User.objects.get(email=username)
except User.DoesNotExist:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = None
if user:
return user if user.check_password(password) else None
else:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
#forms.py
#replaces the normal username CharField with an EmailField
from django import forms
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(AuthenticationForm):
username = forms.EmailField(max_length=75, label='Email')
next = forms.CharField(widget=forms.HiddenInput)
Hope that helps!
With Django 2.0 it's even simpler to achive this.
Create your own UserModel
class User(AbstractBaseUser, PermissionsMixin):
and set the USERNAME_FIELD = 'email'
For reference: Documentation