Issue with custom backend and forms to login

Hi everyone,

I decided to rewrite my basic “Singup/Signin” app in order to understand more about Django.

I wrote my own forms with my own check password function etc.

Thus, after the user is authenticated, the login function doesn’t work, do you have an idea why ?

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager

class UserManager(BaseUserManager):
    def get_by_natural_key(self, username):
        try:
            user = User.objects.get(email=username)
            print("GET BY NATURAL KEY => " + str(user))
        except:
            return None
        else:
            return user
    
    def get_by_id(self, id):
        try:
            user = User.objects.get(pk=id)
            print("GET BY ID => " + str(user))
        except:
            return None
        else:
            return user
            
class User(AbstractUser):
    username = None
    email = models.EmailField(max_length=40, unique=True, blank=False)
    password = models.CharField(max_length=40, blank=False)
    objects = UserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.email

signin function in views.py

def signinUser(request):
    if request.method == "POST":
        form = signinUserForm(request.POST)

        if form.is_valid():
            user = authenticate(request,
                                username=form.cleaned_data["email"],
                                password=form.cleaned_data["password"],)
        
            print("USER IS => " + str(user))
            if user is not None:
                login(request, user)
                return redirect("app_base:index")
            else:
                form.add_error(None,"ERROR : Incorrect email or password")
        else:
            form.add_error(None,"ERROR : Invalid form please retry")
    else:
        form = signinUserForm()
        
    return render(request, "app_accounts/signin.html", {"form":form})

signin.html

{% block page_content %}
<div id="page_navbar">
    <a href="{% url 'app_accounts:signupUser' %}">Signup</a>
    {% if not request.user.is_authenticated %}
    <a href="{% url 'app_accounts:signinUser' %}">Signin</a>
    {% else %}
    <a href="{% url 'app_accounts:logoutUser' %}">Logout</a>
    {% endif %}
</div>
{% endblock %}

My output is :

[28/Nov/2020 10:39:55] "GET /favicon.ico HTTP/1.1" 404 2349
[28/Nov/2020 10:39:56] "GET /accounts/signin HTTP/1.1" 200 647
USER IS => test@mail.com
[28/Nov/2020 10:40:01] "POST /accounts/signin HTTP/1.1" 302 0
[28/Nov/2020 10:40:01] "GET / HTTP/1.1" 200 340

So according to my output, the user is authenticated, I’m redirected to the index view (home page) indeed but the “Signout” menu is not shown whereas I configured the index.html…

We need to see the view that you are redirecting to after being logged in to ensure that your template context is matching your template.

Hi @KenWhitesell, here it is :

from django.shortcuts import render

def index(request):
    return render(request, "app_base/index.html")

Ok, there still more things to check -

  • Is your authenticate function returning a User object?

  • In your view, are you calling the system authenticate function or your backend authenticate function?

    • If you’re calling your authenticate function directly, is it setting the backend attribute on the user?
  • Do you have multiple authentication backends defined, or just the one?

  • [Late addition] Do you have your get_user method defined in your backend?

  • Are you using a custom login method or the standard system login method?

  • What does your index.html template look like? (You posted signin.html but your redirected page is rendering index.html.)

The session attribute on a request is a “a dictionary-like object”. It might be worth printing request.session after the login function is called to verify the key session variables have been set.
From login in django.contrib.auth.__init__.py:

request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = backend
request.session[HASH_SESSION_KEY] = session_auth_hash
  • Is your authenticate function returning a User object?
    => Yes, this is why I get it in the output

  • In your view, are you calling the system authenticate function or your backend authenticate function?
    => My backend function (I checked it)

  • If you’re calling your authenticate function directly, is it setting the backend attribute on the user?

Here is my backends.py file :

from django.contrib.auth.backends import BaseBackend
from .models import User

class EmailBackend(BaseBackend):
    def authenticate(self, request, username=None, password=None):
        try:
            user = User.objects.get(email=username)
        except User.DoesNotExist:
            return None
        else:
            return user
  • Do you have multiple authentication backends defined, or just the one?
    => Just one

  • [Late addition] Do you have your get_user method defined in your backend?
    => No, just in my models.py in the UserManager

  • Are you using a custom login method or the standard system login method?
    => The standard login method (I think the issue is here as the login method uses “username” by default)

  • What does your index.html template look like? (You posted signin.html but your redirected page is rendering index.html.)

{% extends "layout.html" %}

{% block page_title %}Home{% endblock %}

{% block page_content %}
<div id="page_navbar">
    <a href="{% url 'app_accounts:signupUser' %}">Signup</a>
    {% if not user.is_authenticated %}
    <a href="{% url 'app_accounts:signinUser' %}">Signin</a>
    {% else %}
    <a href="{% url 'app_accounts:logoutUser' %}">Logout</a>
    {% endif %}
</div>
{% endblock %}
  • The session attribute on a request is a “a dictionary-like object”. It might be worth printing request.session after the login function is called to verify the key session variables have been set.
    => Well I’ve tried to iterate over it but always got a Key Error :
        if form.is_valid():
            user = authenticate(request,
                                username=form.cleaned_data["email"],
                                password=form.cleaned_data["password"],)
        
            print("USER IS => " + str(user))
            if user is not None:
                login(request, user)
                for item in request.session:
                    print(item)
                return redirect("app_base:index")

or

        if form.is_valid():
            user = authenticate(request,
                                username=form.cleaned_data["email"],
                                password=form.cleaned_data["password"],)
        
            print("USER IS => " + str(user))
            if user is not None:
                login(request, user)
                for item in request.session:
                    print(request.session[item])
                return redirect("app_base:index")

For the purpose of being comprehensive, no, it’s not necessarily why you’re getting it in your output. If your authenticate method returned just the username and not the user object, your output would have been the same. (I can see that it’s not doing that, but I wanted to point out why I needed to ask the question.)

And that’s the problem. See Writing an authentication backend, first paragraph. (Note: This has been pointed out to you before: Django's template doesn't render as expected (template language issue) - #11 by CodenameTim and Django's template doesn't render as expected (template language issue) - #13 by CodenameTim)