Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
API authentication: Default failed authentication should return 403 not 401 #117
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...
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
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).
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
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