Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unauthorized response to POST request in Django Rest Framework with JWT Token

I am building a REST API with Django Rest Framework. I currently have an issue where some of my endpoints return HTTP 401 Unauthorized, whereas the vast majority of my endpoints return correct responses. For authentication I am using JWT tokens with djangorestframework-simplejwt.

I've configured Django to use token auth with djangorestframework-simplejwt.

# rest framework config settings
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
        # 'rest_framework.permissions.AllowAny',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],

The vast majority of my endpoints return valid data when I pass a valid access token in the request. If I do not send a valid token, I receive a HTTP 403.

On the other hand, I have some custom API views which return a HTTP 401 regardless of whether I pass a valid token or not.

I have included the code to one of my problematic views below.

class CheckDifferentialView(generics.GenericAPIView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [TokenAuthentication]
    serializer_class = QuizDifferentialSerializer

    def post(self, request, *args, **kwargs):
        """
            A function to check a quiz question and to update a user's record of questions answered
        """
        print(request.META)
        if 'answer' not in request.data:
            return JsonResponse({'Error': 'answer not found in request'}, status=status.HTTP_400_BAD_REQUEST)

        answer = get_object_or_404(Differential, pk=request.data['answer'])
        serializer = QuizDifferentialSerializer(answer)

        if answer.is_correct:
            pass
            # record correct results
        else:
            pass
            # record failed result

        return Response(serializer.data, status=status.HTTP_200_OK)

And here is my script which I am using to test my API

import requests
import json

POST_LOGIN_URL = 'http://localhost:8000/api/v1/token/'
POST_URL= 'http://localhost:8000/api/v1/check_differential'
REQUEST_URL = 'http://localhost:8000/api/v1/users'

with requests.Session() as session:
    post = session.post(POST_LOGIN_URL, json={"username": "j", "monkey": "aphextwin21"})
    token = json.loads(post.text)['access']
    headers = {'Authorization': 'Bearer ' + token}

    r = session.post(POST_URL, headers=headers, json={"answer": "2"})
    # r = session.get(REQUEST_URL, headers=headers)
    print(token)

print(r.text, r.status_code)

The desired behaviour is that if I send a POST request with a valid token to this endpoint that is would authorise me and carry on with its day. If no Authorization header with a valid access token is given, then I expect it to deny the request.


Update


Enthusiastic Martin kindly point out that

authentication_classes = [TokenAuthentication]

Was overriding the defaults found in my settings file. I was not aware that as far as Django is concerned TokenAuthentication and JWTAuthentication are treated differently. Now I know.

After removing the authentication_classess = [TokenAuthentication] from my views, the views are working as they should.

like image 736
anowlinorbit Avatar asked Oct 18 '19 10:10

anowlinorbit


People also ask

What is JWT token in Django REST framework?

Simple JWT provides a JSON Web Token authentication backend for the Django REST Framework. It aims to cover the most common use cases of JWTs by offering a conservative set of default features. It also aims to be easily extensible in case a desired feature is not present.


Video Answer


1 Answers

The view's authentication class is explicitly set to TokenAuthentication only. It wont work with JWT token.

 authentication_classes = [TokenAuthentication]

You either remove this to let the default classes handle it or change it to accept JWTAuthentication.

like image 83
Enthusiast Martin Avatar answered Nov 01 '22 09:11

Enthusiast Martin