1

I'm a newbie in django and I'm having some problems, what I'm trying to do is a simple login system in Django with a custom backend and using postgresql as the main db.

The problem is, my authentication and login function is apparently working normally but the user is not actually logged in, I wrote a custom message to let me know when the user is logged in and my index is protected against anonymous user, so I basically can't access.

This code from my views.py

@login_required(login_url='signin')
def index(request):
    return render(request, 'index.html')

def signin(request):
    now = datetime.now()
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']

        print(username, password)

        user = UserBackend.authenticate(UserBackend(), username=username, password=password)

        if user is not None:
            login(request, user, backend='core.backends.UserBackend')
            print('Logged In') #### This print/checking is working fine!
            return redirect('/')  
            #return render(request, 'index.html')
        else:
            messages.info(request, 'Invalid Username or Password! Try again')
            return redirect("signin")
    #else:
        return render(request,'signin.html')

    
    #user = auth.authenticate(username=username, password=password)
    return render(request, 'signin.html')

This is my user class from models.py

class user(AbstractBaseUser):
    id = models.AutoField(primary_key=True)
    username = models.TextField()
    real_name = models.TextField()
    email = models.TextField()
    password = models.TextField()
    description = models.TextField()
    last_login = models.DateField()
    created_at = models.DateField()
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)


    class Meta:
        managed = False
        db_table = 'user'
        
   
    def __str__(self) -> str:
        return self.username

And my user backend from backends.py

class UserBackend(ModelBackend):

    def authenticate(self, **kwargs):
        username = kwargs['username']
        password = kwargs['password']

        print('user: ', username)
        print('pass: ', password)
        #try:
        user_ = user.objects.get(username=username)
        
        try:
            print(user_.check_password(password))
            if user_.check_password(password) is True:
                return user_
        except user.DoesNotExist:
            pass

        def get_user(self, user_id):
            try:
                return user.objects.get(pk=user_id)
            except user.DoesNotExist:
                return None

I tried changing the return on views.py to something like

 return redirect(request.GET.get('next')) 

but still not working :(

what should I do?

1 Answer 1

1

The key point, is how you are using your Backend. Although, lets start from the beginning...

Starting with your models, you are unnecessarily overwriting a lot of fields. Django's AbstractUser has these fields you are creating. In fact, the model contains the following fields with validators where appropriate:

  1. username
  2. first_name
  3. last_name
  4. email
  5. is_staff as default false
  6. is_active as default true
  7. date_joined as default timezone.now()
  8. last_login
  9. a built-in function get_full_name (equivalent to your real_name field)

Also, id = models.AutoField(primary_key=True) is not necessary Django by default creates this kind of field automatically for every model. Most of the times used when you want a different kind of field as PK. Thus a simple model, would fulfill your requirements:

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

class User(AbstractUser):
    description = models.TextField()

The backend is pretty straight forward, check credentials and return an User object instance if everything is correct. ModelBackend already has a get_user method defined.

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model

User = get_user_model()

class UserBackend(ModelBackend):
    def authenticate(self, request, **kwargs):
        username = kwargs['username']
        password = kwargs['password']
        
        try:
            user = User.objects.get(username=username)
            pwd_valid = check_password(password, user.password)
            
            if  pwd_valid:
                return user
            else:
                return None
            
        except User.DoesNotExist:
            return None

Now, about on how to use it. Quotting the documentation:

Behind the scenes, Django maintains a list of “authentication backends” that it checks for authentication. When somebody calls django.contrib.auth.authenticate() Django tries authenticating across all of its authentication backends. If the first authentication method fails, Django tries the second one, and so on, until all backends have been attempted.

The list of authentication backends to use is specified in the AUTHENTICATION_BACKENDS setting. This should be a list of Python path names that point to Python classes that know how to authenticate. These classes can be anywhere on your Python path.

So, first we need to add AUTHENTICATION_BACKENDS in settings.py:

AUTH_USER_MODEL = 'core.User'
LOGIN_URL = '/signin'

AUTHENTICATION_BACKENDS = [
    'core.auth.backends.UserBackend', 
    'django.contrib.auth.backends.ModelBackend'
]

And lastly in views.py, we call the functions exactly as you would in a normal login function:

from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from django.contrib import messages

# Create your views here.
@login_required
def index(request):
    return render(request, 'index.html')

def signin(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']

        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            # Confirm that backend is UserBackend
            print(user.backend)
            return redirect('core:index')
        else:
            messages.info(request, 'Invalid Username or Password! Try again')
            return redirect("core:signin")

    return render(request, 'signin.html')
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.