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
Advertisement
Answer
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 ',]