Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- Follows RFC 7235, where authentication failures should return 401.
- Clearer for clients: signals an auth issue instead of suggesting the endpoint is missing.

### Bugfixes
- Fixed OpenAPI schema generation for token obtain endpoints by explicitly
declaring dynamically returned fields (`access`, `refresh`) as read-only
serializer fields.

## 5.5.1

Expand Down
4 changes: 4 additions & 0 deletions rest_framework_simplejwt/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ def get_token(cls, user: AuthUser) -> Token:
class TokenObtainPairSerializer(TokenObtainSerializer):
token_class = RefreshToken

# Dynamically add read-only fields for schema generation only
access = serializers.SerializerMethodField(read_only=True)
refresh = serializers.SerializerMethodField(read_only=True)

def validate(self, attrs: dict[str, Any]) -> dict[str, str]:
data = super().validate(attrs)

Expand Down
39 changes: 39 additions & 0 deletions tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,45 @@ def test_it_should_produce_a_json_web_token_when_valid(self):
AccessToken(s.validated_data["access"])
RefreshToken(s.validated_data["refresh"])

def test_access_and_refresh_fields_are_read_only_for_schema(self):
"""
Ensure 'access' and 'refresh' fields are read-only so
automatic schema generators (drf-yasg, drf-spectacular) detect them.
"""
serializer = TokenObtainPairSerializer()
# access and refresh should exist
self.assertIn("access", serializer.fields)
self.assertIn("refresh", serializer.fields)
# read-only ensures schema generators know they are output-only
self.assertTrue(serializer.fields["access"].read_only)
self.assertTrue(serializer.fields["refresh"].read_only)

def test_schema_fields_do_not_break_runtime_validation(self):
"""
Ensure the patch doesn't interfere with normal validation and token creation.
"""
serializer = TokenObtainPairSerializer(
context=MagicMock(),
data={
TokenObtainPairSerializer.username_field: self.username,
"password": self.password,
},
)
self.assertTrue(serializer.is_valid())
validated_data = serializer.validated_data
self.assertIn("access", validated_data)
self.assertIn("refresh", validated_data)

# Tokens should be valid
access_token = AccessToken(validated_data["access"])
refresh_token = RefreshToken(validated_data["refresh"])

# Validate token type and some claims, but don't assert JTI equality
self.assertEqual(access_token["token_type"], "access")
self.assertEqual(refresh_token["token_type"], "refresh")
self.assertEqual(access_token["user_id"], str(self.user.id))
self.assertEqual(refresh_token["user_id"], str(self.user.id))


class TestTokenRefreshSlidingSerializer(TestCase):
def setUp(self):
Expand Down