Update obtain_auth_token to authenticate using 'USERNAME_FIELD' and 'password' instead of 'username' and 'password' for both the built-in and custom User models

This commit is contained in:
Partho Debnath 2025-03-31 02:23:56 +06:00
parent ac50cec76c
commit 9dc26825bf
3 changed files with 42 additions and 20 deletions

View File

@ -220,7 +220,7 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you use a `custom User` model as `AUTH_USER_MODEL` in `settings.py`, authentication will use the `USERNAME_FIELD` and `password` defined in your custom model.
By default, there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class, By default, there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class,
and include them using the `throttle_classes` attribute. and include them using the `throttle_classes` attribute.

View File

@ -1,32 +1,50 @@
from django.contrib.auth import authenticate from django.contrib.auth import authenticate, get_user_model
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
USER_MODEL = get_user_model()
class AuthTokenSerializer(serializers.Serializer): class AuthTokenSerializer(serializers.Serializer):
username = serializers.CharField( def __init__(self, instance=None, data=None, **kwargs):
label=_("Username"), super().__init__(instance, data=data, **kwargs)
write_only=True self.identifier_fiend_name = USER_MODEL.USERNAME_FIELD
) if USER_MODEL.get_email_field_name() == self.identifier_fiend_name:
password = serializers.CharField( self.fields[self.identifier_fiend_name] = serializers.EmailField(
label=_("Password"), label=_(self.identifier_fiend_name.title()),
style={'input_type': 'password'}, write_only=True
trim_whitespace=False, )
write_only=True else:
) self.fields[self.identifier_fiend_name] = serializers.CharField(
label=_(self.identifier_fiend_name.title()),
write_only=True
)
self.fields["password"] = serializers.CharField(
label=_("Password"),
style={'input_type': 'password'},
trim_whitespace=False,
write_only=True
)
token = serializers.CharField( token = serializers.CharField(
label=_("Token"), label=_("Token"),
read_only=True read_only=True
) )
def validate(self, attrs): def validate(self, attrs):
username = attrs.get('username') identifier_value = attrs.get(self.identifier_fiend_name)
password = attrs.get('password') password = attrs.get('password')
if username and password: if identifier_value and password:
user = authenticate(request=self.context.get('request'), credentials = {
username=username, password=password) self.identifier_fiend_name: identifier_value,
"password": password,
}
user = authenticate(
request=self.context.get('request'),
**credentials,
)
# The authenticate call simply returns None for is_active=False # The authenticate call simply returns None for is_active=False
# users. (Assuming the default ModelBackend authentication # users. (Assuming the default ModelBackend authentication
@ -35,7 +53,7 @@ class AuthTokenSerializer(serializers.Serializer):
msg = _('Unable to log in with provided credentials.') msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg, code='authorization') raise serializers.ValidationError(msg, code='authorization')
else: else:
msg = _('Must include "username" and "password".') msg = _(f'Must include "{self.identifier_fiend_name}" and "password".')
raise serializers.ValidationError(msg, code='authorization') raise serializers.ValidationError(msg, code='authorization')
attrs['user'] = user attrs['user'] = user

View File

@ -1,3 +1,5 @@
from django.contrib.auth import get_user_model
from rest_framework import parsers, renderers from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.authtoken.serializers import AuthTokenSerializer
@ -16,15 +18,17 @@ class ObtainAuthToken(APIView):
serializer_class = AuthTokenSerializer serializer_class = AuthTokenSerializer
if coreapi_schema.is_enabled(): if coreapi_schema.is_enabled():
USER_MODEL = get_user_model()
identifier_field_name = USER_MODEL.USERNAME_FIELD
schema = ManualSchema( schema = ManualSchema(
fields=[ fields=[
coreapi.Field( coreapi.Field(
name="username", name=identifier_field_name,
required=True, required=True,
location='form', location='form',
schema=coreschema.String( schema=coreschema.String(
title="Username", title=identifier_field_name.title(),
description="Valid username for authentication", description=f"Valid {identifier_field_name} for authentication",
), ),
), ),
coreapi.Field( coreapi.Field(