2👍
I would suggest standardizing your Viewset:
class OrderViewSet(viewsets.ModelViewSet):
serializer_class = OrderSerializer
model = Order
def get_queryset(self):
"""QuerySet for this entire ModelViewSet will only return
orders which are opened.
"""
return Order.objects.opened()
@permission_classes((IsAuthenticated,))
@authentication_classes((TokenAuthentication,))
def list(self, request, *args, **kwargs):
return super(OrderViewSet, self).list(request, *args, **kwargs)
Upon further investigation, I took a look at the source code for TokenAuthentication, and it appears that if you don’t send in an authentication token at all, the authenticate()
method returns None
if the get_authorization_header()
method returns nothing. Thus, if you entirely remove the HTTP_AUTHORIZATION
from the header, this is the expected behavior.
I believe the intention here is to not raise an exception so that authentication can move on to the next possible authentication class. If this is not what you want to do, you can override the authenticate()
method in your own class inherited from TokenAuthentication
. See the code below.
def get_authorization_header(request):
"""
Return request's 'Authorization:' header, as a bytestring.
Hide some test client ickyness where the header can be unicode.
"""
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, type('')):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth
class TokenAuthentication(BaseAuthentication):
"""
Simple token based authentication.
Clients should authenticate by passing the token key in the "Authorization"
HTTP header, prepended with the string "Token ". For example:
Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
"""
model = Token
"""
A custom token model may be used, but must have the following properties.
* key -- The string identifying the token
* user -- The user to which the token belongs
"""
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
return None
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(auth[1])
def authenticate_credentials(self, key):
try:
token = self.model.objects.get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed('Invalid token')
if not token.user.is_active:
raise exceptions.AuthenticationFailed('User inactive or deleted')
return (token.user, token)
def authenticate_header(self, request):
return 'Token'
Finally, to make your token fail with a 401
instead of a 200
, you can do:
class YourCustomTokenAuthentication(TokenAuthentication):
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = 'Invalid token header. Token string should not contain spaces.'
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(auth[1])