How Does Admin panel authentication works internally ?

Hello there,

This is my first time writing here.

I’ve been learning Django lately and trying to develop a small app. I encountered a situation where I needed email-based authentication, so in my user model, I used something like this:

class User(AbstractUser):
    email = models.EmailField(null=True, unique=True)
    USERNAME_FIELD = 'email'

In my registration view, I have this functionality:

user.save()
login(request, user)
return redirect('home')

When I sign up a user, it automatically logs them in and redirects to the home page. From my research, I understand this happens because login() logs in the trusted user object I just created and saved in the database.

The problem arises when I try to log in manually: I realized I need a custom authentication backend for email login since Django doesn’t provide built-in email-based authentication. I created a custom backend, and it worked just fine.

My question is: before I created that backend, I could already log in using the email in the Django admin panel. How does the admin panel’s authentication work internally in this case? I haven’t been able to find clear articles about it online.

Thanks in advance!

Hello there!

What do you mean “manually”? Can you provide which view are you using for the login? If it’s a custom view, also share the template being rendered.

1 Like

by manually i meant entering username and password also tried email and password but i figured out later that i need custom backend to authenticate with password
but the question is why didn’t this happen with the login form also ?
it was behaving normally and i didn’t need to write a backend end for it so i was asking how admin panel authenticate users

No you don’t.

Yes, it does support this.

Please post the form, view, and template that you are using for your login page, and we can help you find where the issue(s) are.

It uses the system default django.contrib.auth.views.LoginView with a default template in admin/login.html and the django.contrib.admin.forms.AdminAuthenticationForm. You can see the view being used is the django.contrib.admin.sites.AdminSite.login method.

1 Like

Yep

Most likely it’s related to your form “username” field name

1 Like

this is the template


            <form class="form" action="" method="POST">
              {% csrf_token %}
              <div class="form__group form__group">
                <label for="room_name">Username</label>
                <input id="username" name="username" type="text" placeholder="e.g. jim_cat" />
              </div>
              <div class="form__group">
                <label for="password">Password</label>
                <input
                  id="password"
                  name="password"
                  type="password"
                  placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;"
                />
              </div>

              <button class="btn btn--main" type="submit">
                <svg
                  version="1.1"
                  xmlns="http://www.w3.org/2000/svg"
                  width="32"
                  height="32"
                  viewBox="0 0 32 32"
                >
                  <title>lock</title>
                  <path
                    d="M27 12h-1v-2c0-5.514-4.486-10-10-10s-10 4.486-10 10v2h-1c-0.553 0-1 0.447-1 1v18c0 0.553 0.447 1 1 1h22c0.553 0 1-0.447 1-1v-18c0-0.553-0.447-1-1-1zM8 10c0-4.411 3.589-8 8-8s8 3.589 8 8v2h-16v-2zM26 30h-20v-16h20v16z"
                  ></path>
                  <path
                    d="M15 21.694v4.306h2v-4.306c0.587-0.348 1-0.961 1-1.694 0-1.105-0.895-2-2-2s-2 0.895-2 2c0 0.732 0.413 1.345 1 1.694z"
                  ></path>
                </svg>

                Login
              </button>
            </form>

the view

def loginPage(request):
    page = 'login'
    if request.user.is_authenticated:
        return redirect('home')

    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

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

        if user is not None:
            login(request, user)
            return redirect('home')
        else:
            messages.error(request, 'Username or Password does not exist')

    context = {'page': page}
    return render(request, "base/login.html", context)

and this is the User model


class User(AbstractUser):
    name = models.CharField(max_length=200, null=True)
    email = models.EmailField(unique=True, null=True)
    bio = models.TextField(null=True)

    avatar = models.ImageField(null=True, default="avatar.svg")

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

and this is the user creation form because most likely i didn’t create one for the login

class UserCreationFormClone(UserCreationForm):
    class Meta:
        model = User
        fields = ['name', 'username', 'email', 'password1', 'password2']

Right, its indeed what I thought it was (almost).
The only problem here is your authenticate call.

Since your username field is email that’s the keyword argument you should be passing to authenticate. Here, have a look on the implementation of the Django ModelBackend authentication class.

Notice that if you don’t provide a username keyword argument, then it will look for the UserModel.USERNAME_FIELD on the keyword arguments.
So, what you should be doing here, is provide email (the value of USERNAME_FIELD`) as the keyword argument. That would look like:

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

Just be aware that you’re doing way more work than you actually need to, Django has built-in views for login/logout. They’re documented on Using the Django authentication system → Authentication Views. It’s not wrong to do what you’re doing, specially in the beggining Django can seem to do “too much magic” and doing things on your own first is good to see where you gone wrong, just know that working with the framework, is better to working against it. Learn to use the tools Django provides, such as forms, and use them extensively, the more you use them, more they will make sense. Just keep it learning :flexed_biceps:

Hope that helps

1 Like

Thank you for the clarification, that makes sense now. I appreciate your help <3