Add machine-readable status to Authentication classes

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
<headers snipped>

{
    "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
<headers snipped>

{
    "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.
This commit is contained in:
Georg Lukas 2020-04-08 15:33:07 +02:00
parent b1004a4733
commit cfd9a7e90a

View File

@ -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)