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:
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.
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
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.
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