Skip to content

Status code 403 and text from 401 error #9692

Open
@TooMuchInLove

Description

@TooMuchInLove

https://github.com/encode/django-rest-framework/blob/78e97074e7c823ef9c693e4b63ac1e5c6e41ff81/rest_framework/views.py#L491C1-L518C29

Hey guys!
Such a situation has arisen. When I tried to execute a request in my API (being unauthorized), I received a corresponding message stating that I could not do this without authorization.
But I noticed that the text of the message in the response is from error 401, and the error code is from 403.
DRF version djangorestframework >= 3.14, < 3.15.

Problem description:

The whole point is that BasicAuthentication has an implementation of the authenticate_header method (this method is not implemented in the SessionAuthentication class, it simply inherits from BaseAuthentication'). And at the key moment of the program execution, the APIView class (ViewSetinherits fromAPIView) and the get_authenticate_headermethod receive a tuple of theBasicAuthenticationandSessionAuthenticationelements/classes passed toDEFAULT_AUTHENTICATION_CLASSES (settings.py).
Next, the first class from this tuple is strictly taken, and in turn the method authenticate_header is used (in the case of the BasicAuthentication class, the specific value is returned, and the SessionAuthentication class returns `None').

Example (Response):

Code	Details
403      Error: Unauthorized
Undocumented
{
  "type": "client_error",
  "errors": [
    {
      "code": "not_authenticated",
      "detail": "Authentication credentials were not provided.",
      "attr": null
    }
  ]
}

Is this a mistake or was it originally intended that way (and can it be fixed at the library level)?

Here's how I managed to solve my problem, but it looks like a crutch:

class MyViewSet(ViewSet):
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)
            print('# dispatch `initial`')

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)
            print(f'# dispatch `initial`: response: {response}')

        except Exception as exc:
            response = self.handle_exception(exc)

            # BEGIN
            # This code --->
            if response.status_code == status.HTTP_403_FORBIDDEN:
                if response.data:
                    errors = response.data['errors']
                    if errors:
                        if errors[0]['detail'] == drf_exceptions.NotAuthenticated.default_detail:
                            response.status_code = status.HTTP_401_UNAUTHORIZED
            # END <---

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions