3

I am running into a weird issue with Django CSRF tokens in my SPA.

When I render my application I ensure that the token is set using ensure_csrf_cookie. When I check my cookies I see that csrftoken is properly stored, and I'm also sending it as part of the request using:

     headers: {
       ‘X-CSRFToken’: getCsrfTokenFromCookie(),
     }

When I delete the csrfToken cookie and refresh the page, a new csrfToken is set but when I try to send my request, I get the error

message: "CSRF Failed: CSRF Token missing or incorrect."

But if I refresh the page again, the request sends successfully with the same csrfToken cookie!

I am not sure what is causing this inconsistency. Any thoughts?

linda
  • 105
  • 2
  • 7
  • 2
    please read this H[ow to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) – badiya Aug 07 '17 at 16:04

3 Answers3

1

Your issue may be similar to this question. If you force a full page refresh your issue may be solved.

0

I suppose this issue was very specific to my application. With a React/Redux frontend and Django REST framework, the issue turned out that when the request is instantiated, the csrfToken was not yet set.

linda
  • 105
  • 2
  • 7
0

I found the location.reload() approach made my SPA (React+Redux front end, DRF backend) look bad/clunky as it would log the user in, then refresh the page (tried using it in different spots but it didn't fix this).

For my situation, both CSRF_USE_SESSIONS and CSRF_COOKIE_HTTPONLY are set to True.

I made the login POST route return a "csrfToken" in the Response payload, and then my authentication reducer, upon processing of the LOGIN_SUCCESS action, would update the hidden csrfmiddlewaretoken input value to the csrfToken so it could then be used for any subsequent requests.

Since you can only get the token if you successfully go through the login route, this should be fine from a security perspective.

template:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Sample</title>
</head>
<body>
    <div id="root"></div>
    {% load static %}
    <script>
       // global vars
       var CSRF_TOKEN = '{{ csrf_token }}';
    </script>
    <script src="{% static "frontend/main.js" %}"></script>
</body>
</html>

view:

class LoginAPI(generics.GenericAPIView):
    serializer_class = LoginSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data

        login(request, user)

        return Response({
            'csrfToken': get_token(request) # from `django.middleware.csrf`; when called, refreshes the csrf token in the session and returns it
        })

reducer:

...
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
    console.log(`old CSRF_TOKEN: ${CSRF_TOKEN}`);
    CSRF_TOKEN = action.payload.csrfToken;
    return {
        ...state,
        isAuthenticated: true,
        isLoading: false
    }
...