From cfd9a7e90a775efcbd902eff0a2b9fbeba49c01c Mon Sep 17 00:00:00 2001 From: Georg Lukas Date: Wed, 8 Apr 2020 15:33:07 +0200 Subject: [PATCH] Add machine-readable status to Authentication classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BasicAuthentication and TokenAuthentication have two failure cases when they are passed technically valid credentials by an API client: - the passed credentials are not correct - the credentials are correct but the user is inactive In both cases, only a human-readable 'detail' is returned in the 401 body, which is translated according to the site settings: ```json HTTP/1.1 401 Unauthorized { "detail": "Ungültiges Token" } ``` The free-form text and its translation make it impossible for an API consumer to determine the actual reason (inactive user, out of luck; or wrong credentials, try again). This PR adds a machine-readable 'status' field to the response, which can take one of two values: 1. `invalid-credentials` - returned when the passed username, password or token were incorrect. 2. `inactive-user` - returned when the credentials were valid but the user account is disabled. Example: ```json HTTP/1.1 401 Unauthorized { "detail": "Ungültiges Token", "status": "invalid-token" } ``` As this only adds a machine-readable field for the already exposed human-readable 'detail' field, there are no new security implications. --- rest_framework/authentication.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 382abf158..352afe74c 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -98,10 +98,10 @@ class BasicAuthentication(BaseAuthentication): user = authenticate(request=request, **credentials) if user is None: - raise exceptions.AuthenticationFailed(_('Invalid username/password.')) + raise exceptions.AuthenticationFailed({'detail': _('Invalid username/password.'), 'status':'invalid-credentials'}) if not user.is_active: - raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + raise exceptions.AuthenticationFailed({'detail': _('User inactive or deleted.'), 'status':'inactive-user'}) return (user, None) @@ -200,10 +200,10 @@ class TokenAuthentication(BaseAuthentication): try: token = model.objects.select_related('user').get(key=key) except model.DoesNotExist: - raise exceptions.AuthenticationFailed(_('Invalid token.')) + raise exceptions.AuthenticationFailed({'detail': _('Invalid token.'), 'status':'invalid-credentials'}) if not token.user.is_active: - raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) + raise exceptions.AuthenticationFailed({'detail': _('User inactive or deleted.'), 'status':'inactive-user'}) return (token.user, token)