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

API authentication: Default failed authentication should return 403 not 401 #117

Open
rwillmer opened this Issue Apr 8, 2011 · 4 comments

Comments

Projects
None yet
6 participants
@rwillmer

rwillmer commented Apr 8, 2011

From my understanding of the HTTP RFC http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html, returning a 401 expects the client to respond with a WWW-Authenticate header and new credentials.

Whereas if the user has provided an invalid API key or is over the limit, 403 Forbidden looks more appropriate.

Just a thought...

@gingerlime

This comment has been minimized.

Show comment
Hide comment
@gingerlime

gingerlime Aug 28, 2011

Contributor

I just came across the same thing, but there's a way around it. If you look at the code that checks the is_authorized response, it actually allows either a True/False, or a HttpResponse

https://github.com/toastdriven/django-tastypie/blob/master/tastypie/resources.py#L495

So a simple way to raise the correct response is to build an 403 HttpResponse object

def Http403():
    response = render_to_response("403.html")
    response.status_code = 403
    return response

and then call it in your is_authorized, like this:

def is_authorized(self, request, object=None):
        if request and hasattr(request, 'user'):
            # blocking john
            if request.user.name == 'john':
                return Http403()

I still think it would be better if tastypie did it automatically. 403 is definitely the more correct response if the request is not authorized (rather than not authenticated).

Contributor

gingerlime commented Aug 28, 2011

I just came across the same thing, but there's a way around it. If you look at the code that checks the is_authorized response, it actually allows either a True/False, or a HttpResponse

https://github.com/toastdriven/django-tastypie/blob/master/tastypie/resources.py#L495

So a simple way to raise the correct response is to build an 403 HttpResponse object

def Http403():
    response = render_to_response("403.html")
    response.status_code = 403
    return response

and then call it in your is_authorized, like this:

def is_authorized(self, request, object=None):
        if request and hasattr(request, 'user'):
            # blocking john
            if request.user.name == 'john':
                return Http403()

I still think it would be better if tastypie did it automatically. 403 is definitely the more correct response if the request is not authorized (rather than not authenticated).

@jleclanche

This comment has been minimized.

Show comment
Hide comment
@jleclanche

jleclanche Apr 26, 2013

Tastypie's current behaviour is indeed broken; it shouldn't be returning a 401 without a WWW-Authenticate header.

The other authentication methods oblige to this behaviour. Simple fix is to add a WWW-Authenticate = "apikey" header to the response in ApiKeyAuthentication._unauthorized().

Example app-level fix:

from tastypie.authentication import ApiKeyAuthentication as _ApiKeyAuthentication
from tastypie.http import HttpUnauthorized

class ApiKeyAuthentication(_ApiKeyAuthentication):
       def _unauthorized(self):
               response = HttpUnauthorized()
               response["WWW-Authenticate"] = "apikey"
               return response

jleclanche commented Apr 26, 2013

Tastypie's current behaviour is indeed broken; it shouldn't be returning a 401 without a WWW-Authenticate header.

The other authentication methods oblige to this behaviour. Simple fix is to add a WWW-Authenticate = "apikey" header to the response in ApiKeyAuthentication._unauthorized().

Example app-level fix:

from tastypie.authentication import ApiKeyAuthentication as _ApiKeyAuthentication
from tastypie.http import HttpUnauthorized

class ApiKeyAuthentication(_ApiKeyAuthentication):
       def _unauthorized(self):
               response = HttpUnauthorized()
               response["WWW-Authenticate"] = "apikey"
               return response
@istodi

This comment has been minimized.

Show comment
Hide comment
@istodi

istodi Aug 29, 2013

Based on what @adys did, I make a little change only changing the status_code of the response to 403.

from tastypie.authentication import BasicAuthentication as _BasicAuthentication

class BasicAuthentication(_BasicAuthentication):
    def _unauthorized(self):
        response = super(BasicAuthentication, self)._unauthorized()
        response.status_code = 403
        return response

istodi commented Aug 29, 2013

Based on what @adys did, I make a little change only changing the status_code of the response to 403.

from tastypie.authentication import BasicAuthentication as _BasicAuthentication

class BasicAuthentication(_BasicAuthentication):
    def _unauthorized(self):
        response = super(BasicAuthentication, self)._unauthorized()
        response.status_code = 403
        return response

@georgedorn georgedorn added wontfix easy_to_fix and removed wontfix labels Apr 9, 2015

@georgedorn

This comment has been minimized.

Show comment
Hide comment
@georgedorn

georgedorn Apr 9, 2015

Contributor

Marking easy_to_fix because combining @gingerlime's suggestions with a test case would make for a readily-accepted PR.

Contributor

georgedorn commented Apr 9, 2015

Marking easy_to_fix because combining @gingerlime's suggestions with a test case would make for a readily-accepted PR.

@SeanHayes SeanHayes added this to the v0.13.0 milestone Oct 12, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment