Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api-guide/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a

{ '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,
and include them using the `throttle_classes` attribute.
Expand Down
50 changes: 34 additions & 16 deletions rest_framework/authtoken/serializers.py
Original file line number Diff line number Diff line change
@@ -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 rest_framework import serializers

USER_MODEL = get_user_model()
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The USER_MODEL is evaluated at module import time, which means if the user model changes dynamically or if this module is imported before Django apps are ready, it could reference the wrong model. Consider moving this inside the init method where it's actually used, similar to how it's done in the views.py file.

Copilot uses AI. Check for mistakes.


class AuthTokenSerializer(serializers.Serializer):
username = serializers.CharField(
label=_("Username"),
write_only=True
)
password = serializers.CharField(
label=_("Password"),
style={'input_type': 'password'},
trim_whitespace=False,
write_only=True
)
def __init__(self, instance=None, data=None, **kwargs):
super().__init__(instance, data=data, **kwargs)
self.identifier_fiend_name = USER_MODEL.USERNAME_FIELD
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
if USER_MODEL.get_email_field_name() == self.identifier_fiend_name:
self.fields[self.identifier_fiend_name] = serializers.EmailField(
Comment on lines +13 to +14
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
label=_(self.identifier_fiend_name.title()),
write_only=True
)
else:
self.fields[self.identifier_fiend_name] = serializers.CharField(
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
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
)
Comment on lines +10 to +28
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new behavior for supporting custom user models with different USERNAME_FIELD values (e.g., 'email') lacks test coverage. Consider adding tests that verify the serializer correctly handles custom user models where USERNAME_FIELD is set to a field other than 'username', such as 'email'.

Copilot uses AI. Check for mistakes.

token = serializers.CharField(
label=_("Token"),
read_only=True
)

def validate(self, attrs):
username = attrs.get('username')
identifier_value = attrs.get(self.identifier_fiend_name)
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
password = attrs.get('password')

if username and password:
user = authenticate(request=self.context.get('request'),
username=username, password=password)
if identifier_value and password:
credentials = {
self.identifier_fiend_name: identifier_value,
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
"password": password,
}
user = authenticate(
request=self.context.get('request'),
**credentials,
)

# The authenticate call simply returns None for is_active=False
# users. (Assuming the default ModelBackend authentication
Expand All @@ -35,7 +53,7 @@ def validate(self, attrs):
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg, code='authorization')
else:
msg = _('Must include "username" and "password".')
msg = _(f'Must include "{self.identifier_fiend_name}" and "password".')
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: 'identifier_fiend_name' should be 'identifier_field_name'. This typo appears throughout the class and will cause the code to break.

Copilot uses AI. Check for mistakes.
raise serializers.ValidationError(msg, code='authorization')

attrs['user'] = user
Expand Down
10 changes: 7 additions & 3 deletions rest_framework/authtoken/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.contrib.auth import get_user_model

from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
Expand All @@ -16,15 +18,17 @@ class ObtainAuthToken(APIView):
serializer_class = AuthTokenSerializer

if coreapi_schema.is_enabled():
USER_MODEL = get_user_model()
identifier_field_name = USER_MODEL.USERNAME_FIELD
Comment on lines +21 to +22
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The USER_MODEL and identifier_field_name are evaluated at class definition time inside the conditional block. This means they are computed once when the module is imported. If the user model is swapped or changed dynamically, this will not be reflected. Consider moving this logic to a method or property that evaluates at runtime.

Copilot uses AI. Check for mistakes.
schema = ManualSchema(
fields=[
coreapi.Field(
name="username",
name=identifier_field_name,
required=True,
location='form',
schema=coreschema.String(
title="Username",
description="Valid username for authentication",
title=identifier_field_name.title(),
description=f"Valid {identifier_field_name} for authentication",
),
Comment on lines 23 to 32
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new dynamic schema generation based on USERNAME_FIELD lacks test coverage. Consider adding tests that verify the schema correctly reflects the USERNAME_FIELD from custom user models (e.g., when USERNAME_FIELD is 'email', the schema should show 'email' instead of 'username').

Copilot uses AI. Check for mistakes.
),
coreapi.Field(
Expand Down
Loading