79499409

Date: 2025-03-11 00:13:58
Score: 0.5
Natty:
Report link

Django’s default authentication backend (ModelBackend) expects username as the identifier. However, when a custom user model defines email as USERNAME_FIELD, Django does not recognize it during authentication.

To fix this, a custom authentication backend must be implemented as rightly highlighted by @7berlin above.

Step 1: Create a Custom Authentication Backend

Create a new file inside an appropriate app (e.g., users/auth_backend.py) and add:

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

UserModel = get_user_model()

class EmailBackend(ModelBackend):
    """
    Custom authentication backend to authenticate users using email instead of username.
    """

    def authenticate(self, request, email=None, password=None, **kwargs):
        if email is None or password is None:
            return None

        try:
            user = UserModel.objects.get(email=email)
        except UserModel.DoesNotExist:
            return None

        if user.check_password(password) and self.user_can_authenticate(user):
            return user

        return None

Step 2: Register the Custom Authentication Backend

Modify settings.py to tell Django to use this custom authentication backend:

AUTHENTICATION_BACKENDS = [
    'users.auth_backend.EmailBackend',  # Adjust path based on the project structure
    'django.contrib.auth.backends.ModelBackend',  # Keep Django's default
]

Why keep ModelBackend?

If Django’s default authentication (e.g., admin login with username) is still required, keeping ModelBackend as a fallback is a good practice.

Step 3: Update the Login Function

Modify the view handling authentication to use the new backend:

from django.contrib.auth import authenticate, login
from django.contrib.auth.models import update_last_login
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny

@action(detail=False, methods=["post"], permission_classes=[AllowAny])
def login(self, request):
    email = request.data.get("email")
    password = request.data.get("password")

    user = authenticate(request, email=email, password=password)  # Now works correctly
    if user:
        login(request, user)
        update_last_login(None, user)  # Updates last login timestamp
        return Response({"message": "Login successful!"})

    return Response({"error": "Invalid credentials"}, status=401)

Step 4: Restart the Django Server

After making these changes, restart Django to ensure the new authentication backend is applied:

python manage.py migrate
python manage.py runserver

Expected Outcome:

Additional Debugging Tips

If login still fails, check the following:

  1. Ensure migrations are applied (python manage.py migrate)
  2. Confirm that the email exists in the database
  3. Use print(user.check_password(password)) in the backend to verify password hashing
  4. Clear Django sessions (python manage.py shell → from django.contrib.sessions.models import Session; Session.objects.all().delete())

This solution was verified with the help of AI, but tested and refined manually.

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Contains question mark (0.5):
  • User mentioned (1): @7berlin
  • Low reputation (0.5):
Posted by: krtk30