Skip to content

UNAUTHORIZED (401) beeing returned in a view with AllowAny permission #2383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
filipeximenes opened this issue Jan 7, 2015 · 10 comments
Closed

Comments

@filipeximenes
Copy link
Contributor

My project has Token as the default authentication, and my view permissions classes are: AllowAny and a custom permission that for the sake of debugging is always returning False. I'm sending a GET request with no custom headers and receiving 401, with the message:

{
"detail": "Authentication credentials were not provided."
} 

Since my view has the AllowAny permission, I suspect it should never be returning 401, and in the specified situation it should be returning 403 instead.

Not 100% sure if this is a bug since it's not common to have AllowAny permission being used alongside other permissions.

@tomchristie
Copy link
Member

See docs here... http://www.django-rest-framework.org/api-guide/authentication/#unauthorized-and-forbidden-responses

@mrcoles
Copy link

mrcoles commented Apr 3, 2015

Hrm… I think there’s still room for improvement here.

First, I don’t think the text in the docs is clear enough. I see the bolded section:

The first authentication class set on the view is used when determining the type of response.

Something along the lines of the following might be clearer:

The first authentication class set on the view is used when determining the type of response, and any unauthenticated request that fails a permission will return an HTTP 401 Unauthorized status code (even if the particular view does not require authentication).

Second, I don’t think the current functionality is completely right. It seems @filipeximenes (and more recently I) expected that any a view that doesn’t require authentication would not return a 401 when some other permission fails.

It looks like this functionality maybe came in from #416 and the relevant code block in views.py is:

def permission_denied(self, request):
    """
    If request is not permitted, determine what kind of exception to raise.
    """
    if not self.request.successful_authenticator:
        raise exceptions.NotAuthenticated()
    raise exceptions.PermissionDenied()

and within views.py, that method is called from the following permissions checks:

    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(request)

    def check_object_permissions(self, request, obj):
        """
        Check if the request should be permitted for a given object.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_object_permission(request, self, obj):
                self.permission_denied(request)

Doesn’t look like an easy change given how the code is structured.

(Edit) Solution - upon thinking about it a bit more, the best solution for a developer using the existing framework seems to be to raise an explicit exception in the permission itself, e.g.,

from rest_framework import permissions, exceptions

class MyPermission(permissions.BasePermission):
    def has_permission(self, request, view):
        is_ok = some_permission_check()
        if not is_ok:
            raise exceptions.PermissionDenied()
        return is_ok        

This is a little annoying, but gets the job done for people who really want it to work this way. Also, you can pass in an option argument to the PermissionDenied exception to specify the detail text that gets returned in the response.

Btw, complaining aside, fantastic work on this framework! 👏

@ATNC
Copy link

ATNC commented May 8, 2018

I had the same problem. I fixed this 'bug' with added authentication_classes = [] to my view.

class MyView(generics.GenericAPIView):
    authentication_classes = []
    (...)
 

@dheerajmpai
Copy link

dheerajmpai commented Dec 27, 2019

This is a bug!.

My default authorisation is Token Authorization. The view which I am using does not need any authorization.

  • Case 1 : No Authorization
    I get expected output

  • Case 2 : Token Auth but wrong token
    I get Error 401

  • Case 3 : Token Auth with correct token
    I get expected output

@AryanJ-NYC
Copy link

I can confirm and recreate what @dheerajmpai stated above.

@GregoryhClark
Copy link

This is not a bug. It's a logical error. If my user has a token that is being added to the headers for all requests, but that token happens to be expired, they should still be able to access public endpoints. Please fix.

@rpkilby
Copy link
Member

rpkilby commented Aug 11, 2020

If there's a bug in DRF, a good place to start would be a PR with a failing test case that demonstrates the issue.

@microHoffman
Copy link

Hey, any updates on this? I've just encountered this today and it seems to me really confusing.

@drivelous
Copy link

@GregoryhClark ended up solving my error with his comments. My token, which I was storing in localStorage, was expired. My LoginView had permissions AllowAny set correctly and after removing all token info from localStorage I was able to hit my endpoints. Very annoying, but at least I can reproduce it and code against it now

@microHoffman
Copy link

thx for the info @drivelous . Unfortunately for me it gets more difficult to handle since we are using httpOnly cookie for storing the token:/. I still have not found a proper way how to get around this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants