Rayyan
Rayyan

Reputation: 302

Forbidden HTTP 403 DRF + ReactJS request.session

I am working on a Quiz Application using DjangoRestFramework and ReactJS. My App is comprised of two apps, api and frontend. In my api app, I have multiple API views for many different things. One of my API Views is called JoinQuiz. When I call it on my frontend, I get this error in the console:

Forbidden: /api/join-quiz
[16/Dec/2020] "POST /api/join-quiz HTTP/1.1" 403 58

I don't think my problem is due to a CSRF error because my other API views are working perfectly fine. I may be wrong on this point.

[16/Dec/2020] "GET /api/get-question?id=3 HTTP/1.1" 200 10009 
[17/Dec/2020 01:15:47] "POST /api/create-quiz HTTP/1.1" 201 11959

I suspect that my request.session may be the issue because when I go directly to /api/join-quiz and make a POST request with my code, nothing goes wrong and I have a successful post.

Files

Views.py

class QuizView(generics.ListAPIView):
    queryset = Quiz.objects.all()
    serializer_class = QuizSerializer

class GetQuiz(APIView):
    """ Searches for a quiz given its code and returns the Quiz with is_host info"""
    serializer_class = QuizSerializer
    lookup_url_kwarg = 'code'

    def get(self, request, format=None): # This is an HTTP GET request
        code = request.GET.get(self.lookup_url_kwarg)
        if code != None: # Check if code is not equal to None
            quiz = Quiz.objects.filter(code=code)
            if len(quiz) > 0: # If there is a quiz...
                data = QuizSerializer(quiz[0]).data
                data['is_host'] = self.request.session.session_key == quiz[0].host
                

                return Response(data, status=status.HTTP_200_OK)

            return Response({'Quiz not found': 'Invalid Code'}, status=status.HTTP_404_NOT_FOUND)
        return Response({'Bad Request': 'Code Parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
        

        
class CreateQuizView(APIView):
    """Creates A new Quiz given nested question and answer data"""
    serializer_class = QuizSerializer

    def post(self, request, format=None):
        """ Create the User's Account first"""
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()
        
        data = request.data
        data['host'] = self.request.session.session_key
        serializer = self.serializer_class(data=data)

        if serializer.is_valid():
            
            quiz = serializer.create(validated_data=data)
            self.request.session['quiz_code'] = quiz.code

            return Response(
                self.serializer_class(quiz).data, 
                status=status.HTTP_201_CREATED
                )


        return Response({'Bad Request': 'Invalid Data'}, status=status.HTTP_400_BAD_REQUEST)

        
class JoinQuiz(APIView):
    """Join a quiz based on the quiz code"""
    lookup_url_kwarg = 'code'

    def post(self, request, format=None):
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()

        print(self.request.session.session_key)
        code = request.data.get(self.lookup_url_kwarg)

        if code != None:
            quiz_result = Quiz.objects.filter(code=code)
            if len(quiz_result) > 0:

                self.request.session['quiz_code'] = code
                return Response({'message': 'Quiz Joined!'}, status=status.HTTP_200_OK)
            
            return Response({'Quiz Not Found': 'Invalid Quiz Code'}, status=status.HTTP_404_NOT_FOUND)
        
        return Response({'Bad Request': 'Invalid Post Data'}, status=status.HTTP_400_BAD_REQUEST)
        

serializers.py




class QuizSerializer(serializers.ModelSerializer):
    
    questions = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Quiz
        fields = ['id', 'code', 'questions', 'name']

    def create(self, validated_data):
        questions_data = validated_data.pop('questions')

        quiz = Quiz.objects.create(**validated_data)

        for question_data in questions_data:
            
            answers_data = question_data.pop('answers')
            question = Question.objects.create(quiz=quiz, **question_data)

            for answer_data in answers_data: 
                Answer.objects.create(question=question, **answer_data)

        return quiz

Frontend Request

  const quizButtonPressed = () => {
      const requestOptions = {
          method: "POST",
          headers: {"Content-Type": "application/json"},
          body: JSON.stringify({
              code: quizCode
          })
      };
      fetch('/api/join-quiz', requestOptions)
      .then((response) => {
          if (response.ok) {
              props.history.push(`/play/${quizCode}`);
          } else {
              setError("Quiz not found")
          }        
      })
      .catch((error) => console.log(error));
  }

EDIT

I followed the solution from @Blusky and it worked!

Following the Django docs link from @Blusky solved the problem: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

Upvotes: 2

Views: 664

Answers (1)

Blusky
Blusky

Reputation: 3784

Even though you checked it, it might be a CSRF problem. You should check the body of the 403 Error, it might contains further information.

Only when authenticated, POST request on Django requires a CSRF token (it might be why your first POST is working)

If it's the case, you can check this snippet: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

Upvotes: 1

Related Questions