Fix the response status code when authenticating with wrong credentials

This conforms to RFC9110, see
https://www.rfc-editor.org/rfc/rfc9110#section-15.5.2-2
This commit is contained in:
Max Hausch 2025-02-20 12:41:43 +01:00
parent 17e95604f5
commit 754010859b
No known key found for this signature in database
5 changed files with 9 additions and 10 deletions

View File

@ -2,6 +2,7 @@ from django.contrib.auth import authenticate
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
from rest_framework.exceptions import AuthenticationFailed
class AuthTokenSerializer(serializers.Serializer): class AuthTokenSerializer(serializers.Serializer):
@ -32,8 +33,7 @@ class AuthTokenSerializer(serializers.Serializer):
# users. (Assuming the default ModelBackend authentication # users. (Assuming the default ModelBackend authentication
# backend.) # backend.)
if not user: if not user:
msg = _('Unable to log in with provided credentials.') raise AuthenticationFailed()
raise serializers.ValidationError(msg, code='authorization')
else: else:
msg = _('Must include "username" and "password".') msg = _('Must include "username" and "password".')
raise serializers.ValidationError(msg, code='authorization') raise serializers.ValidationError(msg, code='authorization')

View File

@ -463,8 +463,6 @@ class APIView(View):
if auth_header: if auth_header:
exc.auth_header = auth_header exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
exception_handler = self.get_exception_handler() exception_handler = self.get_exception_handler()

View File

@ -281,7 +281,7 @@ class SessionAuthTests(TestCase):
Ensure POSTing form over session authentication without logged in user fails. Ensure POSTing form over session authentication without logged in user fails.
""" """
response = self.csrf_client.post('/session/', {'example': 'example'}) response = self.csrf_client.post('/session/', {'example': 'example'})
assert response.status_code == status.HTTP_403_FORBIDDEN assert response.status_code == status.HTTP_401_UNAUTHORIZED
class BaseTokenAuthTests: class BaseTokenAuthTests:
@ -440,7 +440,7 @@ class TokenAuthTests(BaseTokenAuthTests, TestCase):
{'username': self.username, 'password': "badpass"}, {'username': self.username, 'password': "badpass"},
format='json' format='json'
) )
assert response.status_code == 400 assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_token_login_json_missing_fields(self): def test_token_login_json_missing_fields(self):
"""Ensure token login view using JSON POST fails if missing fields.""" """Ensure token login view using JSON POST fails if missing fields."""
@ -490,7 +490,7 @@ class IncorrectCredentialsTests(TestCase):
permission_classes=() permission_classes=()
) )
response = view(request) response = view(request)
assert response.status_code == status.HTTP_403_FORBIDDEN assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert response.data == {'detail': 'Bad credentials'} assert response.data == {'detail': 'Bad credentials'}

View File

@ -1,6 +1,7 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from rest_framework import status
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.test import APIClient from rest_framework.test import APIClient
@ -21,14 +22,14 @@ class AnonymousUserTests(TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
self.client.get('/basicviewset') self.client.get('/basicviewset')
def test_get_returns_http_forbidden_when_anonymous_user(self): def test_get_returns_http_unauthorized_when_anonymous_user(self):
old_permissions = BasicModelWithUsersViewSet.permission_classes old_permissions = BasicModelWithUsersViewSet.permission_classes
BasicModelWithUsersViewSet.permission_classes = [IsAuthenticated, OrganizationPermissions] BasicModelWithUsersViewSet.permission_classes = [IsAuthenticated, OrganizationPermissions]
response = self.client.get('/basicviewset') response = self.client.get('/basicviewset')
BasicModelWithUsersViewSet.permission_classes = old_permissions BasicModelWithUsersViewSet.permission_classes = old_permissions
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@override_settings(ROOT_URLCONF='tests.browsable_api.auth_urls') @override_settings(ROOT_URLCONF='tests.browsable_api.auth_urls')

View File

@ -132,7 +132,7 @@ class DecoratorTestCase(TestCase):
request = self.factory.get('/') request = self.factory.get('/')
response = view(request) response = view(request)
assert response.status_code == status.HTTP_403_FORBIDDEN assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_throttle_classes(self): def test_throttle_classes(self):
class OncePerDayUserThrottle(UserRateThrottle): class OncePerDayUserThrottle(UserRateThrottle):