Django Authentication to use both email and username in exiting app - python

I am struggling with adding custom AUTHENTICATION_BACKENDS for my exiting app. I have all done with my app but now want to login with username or EmailID. I am successfully able to login with username and password.
now just want to add EmailID as well.
I tried adding below code in my settings.py
AUTHENTICATION_BACKENDS = ( 'authentication.backends.EmailOrUsernameModelBackend', 'django.contrib.auth.backends.ModelBackend', )
and in \authentication\backends.py I have
from django.conf import settings
from django.contrib.auth.models import User
class EmailOrUsernameModelBackend(object):
def authenticate(self, username=None, password=None):
print("Inside EmailorUsernameModelBackend")
if '#' in username:
print("User with Email")
kwargs = {'email': username}
else:
print("User with Username")
kwargs = {'username': username}
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
where my \authentication\views.py
def login_view(request):
form = LoginForm(request.POST or None)
msg = None
if request.method == "POST":
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
print("User=",user)
if user is not None:
login(request, user)
return redirect("dashboard")
else:
msg = 'Invalid credentials'
else:
msg = 'Error validating the form'
return render(request, "/login.html", {"form": form, "msg": msg})
I am trying to print some statement if authenticate method call from EmailOrUsernameModelBackend but none printing, so I guess for some reason it is not calling that method.
please help me what I am missing to call custom authenticate method.

I think the issue is that you're not subclassing BaseBackend from Django, but just the regular python object.
I usually do 2 seperate backends, that makes my code a lot clearer to read for others.
from django.contrib.auth.backends import BaseBackend
class EmailBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
class UsernameBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(username=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None

Related

having trouble getting user authentication to work in Python

I am building a django/react app and having trouble with the backend user authentication, have not been able to debug the issue.
After successfully creating an account I am now trying to login. This is the the login route I have built.
#csrf_exempt
#api_view(['POST'])
def loginUser(request):
data = request.data
if request.method == 'POST':
email = data['email']
password = data['password']
try:
user = User.objects.get(email=email)
except:
message = {'detail': 'email does not match a user'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
username = user.username
user = authenticate(request, username=username, password=password)
# Returning correct username and password
print('This is the username:', username)
print('This is the password:', password)
# returning None, even though username and password are matching the info used for signup. (confirmed on admin page)
print('This is the user:', user)
if user is not None:
login(request, user)
serializer = UserSerializer(user, many=False)
message = {'detail': 'user has been logged in'}
return Response(serializer.data)
else:
message = {'detail': 'Username or password is incorrect'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
Any help would be greatly appreciate it since I have been stuck on this for 2 days.
It may be due to the Try-Except or due to the fact that you use email instead of username (but I dont think that matters). You could try something like this:
from django.contrib.auth import authenticate
from django.contrib.auth.models import auth
def loginUser(request):
if request.method == 'POST':
username = request.POST['name']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
auth.login(request, user)
return redirect('index')
else:
messages.info(request, 'Invalid Username or Password')
return redirect('login')
else:
return render(request, 'login.html')
Hopefully this helps.
If you use this code and the problem still persists, it could be that your template or urls may not be correct.

Django request doesn't save the logged in user

So I'm starting to learn Django authentication.
from django.contrib.auth import login as log_in
def login(request):
...
if request.method == "POST":
form = UserLoginForm(request.POST)
if form.is_valid():
user = User.objects.filter(email=form.cleaned_data["email"])
if user.exists():
user = user.first()
if check_password(
form.cleaned_data["password"], user.password
):
log_in(request,user)
return redirect("/main/")
else:
messages.warning(request, "email/password are incorrect")
else:
messages.warning(request, "User not found")
...
and I'm trying to access the request.user in another view like this:
if request.user.is_authenticated:
#do somthing
but while debugging I found that after the first code log_in() statement the request.user is authenticated, but in the seconed code it's not.
You have to set the authentication backend:
from django.conf import settings
# ...
user.backend = settings.AUTHENTICATION_BACKENDS[0]
log_in(request, user)
I found the problem,
The problem is that I'm using a custom user model with an email attribute instead of a username so I had to build a new Backend to use with my custom model, then added it to the AUTHENTICATION_BACKENDS in the settings.py file.
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from .models import User
class NewBackend(ModelBackend):
def authenticate(self, request, email, password) -> User:
try:
user: User = User.objects.get(email=email)
if user.check_password(password):
return user
else:
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
and in views.py
def login(request: HttpRequest):
if request.user.is_authenticated:
return redirect("/main/")
if request.method == "POST":
form = UserLoginForm(request.POST)
if form.is_valid():
umail=form.cleaned_data['email']
upasswd=form.cleaned_data['password']
user = authenticate(request=request,email=umail,password=upasswd)
if user is not None:
log_in(request,user)
return redirect("/main/")
else:
messages.warning(request, "email/password are incorrect")
form = UserLoginForm()
context = {"title": "Login", "form": form}
return render(request, "login.html", context)
settings.py:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'login_signup.backend.NewBackend',
]

Django differentiate between incorrect login information and inactive user on login

Currently I added in my site a method for email confirmation when registering. What I saw though, is that when the user is registered, but didn't click in the confirmation link yet and tries to login, I can't differentiate between wrong user/password and not confirmed user.
This is my login function:
def loginUser(request):
if request.user.is_authenticated:
return redirect("decks:index")
if request.method == "POST":
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
login(request, user)
return redirect("myApp:portal")
elif user is not None:
messages.error(request, "Email is not confirmed.")
else:
messages.error(request, "Invalid username or password.")
else:
messages.error(request, "Invalid username or password.")
form = AuthenticationForm()
return render(request, "myApp/login.html", context = {'login_form': form})
The problem is that when running form.is_valid() the authenticate function from /home/nowork/.local/lib/python3.8/site-packages/django/contrib/auth ModelBackend is executed:
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
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
So the form.is_valid() will never be true when the is_active flag is False. So I have no way of telling if the combination of user+password is incorrect or the user is not confirmed yet.
I'm not sure what's the correct way of doing this, I thought to do something like:
User.objects.get(username=request.POST['username'])
Can users exploit this somehow? Is there any better way to accomplish this?
Using python3 and Django 4.0
You can make your own CustomLoginBackend as
from django.contrib.auth import get_user_model
class CustomLoginBackend(object):
def authenticate(self, request, username, password):
User = get_user_model()
try:
user = User.objects.using(db_name).get(username=username)
except User.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Then in your views.py
def loginUser(request):
if request.user.is_authenticated:
return redirect("decks:index")
if request.method == "POST":
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active == True:
login(request, user)
return redirect("myApp:portal")
else:
messages.error(request, "Email is not confirmed.")
else:
messages.error(request, "Invalid username or password.")
else:
messages.error(request, "Invalid username or password.")
form = AuthenticationForm()
return render(request, "myApp/login.html", context = {'login_form': form})
And at last don't forgot to add AUTHENTICATION_BACKENDS in your settings.py as
AUTHENTICATION_BACKENDS = ['path_to_your.CustomLoginBackend ',]

Django authenticate by email

I write my own authentication backend in file project/emailauth.py with content:
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class EmailBackend(object):
def authenticate(self, username=None, password=None):
print("Email auth")
try:
user = User._default_manager.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
except:
return None
def get_user(self, user_id):
try:
return User._default_manager.get(pk=user_id)
except User.DoesNotExist:
return None
In my project/settings.py at the end of the file I added the content:
AUTHENTICATION_BACKENDS = (
'project.emailauth.EmailBackend',
'django.contrib.auth.backends.ModelBackend',
)
I use authenticate methon in userprofile/views.py:
from django.contrib.auth import authenticate, login, logout
#...
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
profile = authenticate(username=username, password=password)
if profile is not None:
login(request, profile)
return HttpResponseRedirect(redirect_to)
#...
The most interesting I notice that console output Email auth only when I try to login by username and password if I use email and password there is not output in console. Looks like my custom authenticate fire up after standard method...
Can someone show me where I made a mistake?
It turned out my forms validation block hole login by email (becouse it's check if users exist in database before it try to authenticate)

Multiple login fields in django user

While using Django user is it possible somehow to use multiple login fields?
Like in Facebook where we can login using username, email as well as the phone number.
Yes, it is! You can write your own authentication backend, as this section describes:
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/
We suppose you have created an application account/ where you extend the User model for the additional phone field.
Create your auth backend backends.py inside account/ and write you own authentication logic. For example:
from account.models import UserProfile
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 UserProfile.objects.get(pk=user_id)
except UserProfile.DoesNotExist:
return None
def authenticate(self, username, password):
try:
user = UserProfile.objects.get(
Q(username=username) | Q(email=username) | Q(phone=username)
)
except UserProfile.DoesNotExist:
return None
return user if user.check_password(password) else None
Put you custom backend in you project's settings.py:
AUTHENTICATION_BACKENDS = ('account.backends.AuthBackend',)
Then just authenticate by passing username, email or phone in the POST's username field.
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
...
else:
# Return an 'invalid login' error message.
...
see: https://docs.djangoproject.com/fr/1.8/topics/auth/default/

Categories

Resources