Home > database >  RTK Query and Laravel Sanctum: CSRF Token Mismatch (Request has cookie)
RTK Query and Laravel Sanctum: CSRF Token Mismatch (Request has cookie)

Time:12-04

I'm implementing a Laravel API React SPA with Sanctum authentication.

With Sanctum, before requesting the actual login route, you need to send a request to /sanctum/csrf-cookie 'to initialize csrf protection'.

Currently I have this RTK Query api:

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { API_HOST } from "./config";

export const authApi = createApi({
  reducerPath: "authApi",
  baseQuery: fetchBaseQuery({
    baseUrl: `${API_HOST}`,
  }),
  endpoints: (builder) => ({
    initCsrf: builder.mutation<void, void>({
      query() {
        return {
          url: "sanctum/csrf-cookie",
          credentials: "include",
          headers: {
            "X-Requested-With": "XMLHttpRequest",
            "Content-Type": "application/json",
            Accept: "application/json",
          },
        };
      },
    }),
    loginUser: builder.mutation<{ access_token: string; status: string }, { username: string; password: string }>({
      query(data) {
        return {
          url: "login",
          method: "POST",
          body: data,
          credentials: "include",
          headers: {
            "X-Requested-With": "XMLHttpRequest",
            "Content-Type": "application/json",
            Accept: "application/json",
          },
        };
      },
      async onQueryStarted(args, { dispatch, queryFulfilled }) {
        try {
          await queryFulfilled;
        } catch (err) {
          console.error(err);
        }
      },
    }),
    logoutUser: builder.mutation<void, void>({
      query() {
        return {
          url: "logout",
          credentials: "include",
        };
      },
    }),
  }),
});

export const { useLoginUserMutation, useLogoutUserMutation, useInitCsrfMutation } = authApi;

Then in my login page, when the user clicks the login button, I call:

const onSubmitHandler: SubmitHandler<LoginInput> = (values) => {
    initCsrf()
      .then(() => {
        loginUser(values);
      })
      .catch((err) => {
        console.error(err);
      });
  };

The first request is working and sets the cookie but the second returns with a 419 CSRF Token mismatch exception.

Examining the requests, the login request contains the XSRF-TOKEN cookie with the token that I got in the first request so it should work ok.

This worked before with Axios using the same structure (first request to establish the cookie and the second including the cookie).

CodePudding user response:

You are sending the CSRF token as a cookie, but Laravel expects it to be sent as a X-XSRF-TOKEN header.

Update your loginUser endpoint to include the token in the headers instead of sending it as a cookie:

loginUser: builder.mutation<{ access_token: string; status: string }, { username: string; password: string }>({
    query(data) {
    return {
    url: "login",
    method: "POST",
    body: data,
    headers: {
    "X-Requested-With": "XMLHttpRequest",
    "Content-Type": "application/json",
    Accept: "application/json",
    "X-XSRF-TOKEN": document.cookie.match(/XSRF-TOKEN=([^;] )/)[1], // add this line
    },
    };
    },
    async onQueryStarted(args, { dispatch, queryFulfilled }) {
    try {
    await queryFulfilled;
    } catch (err) {
    console.error(err);
    }
    },
    }),

extract the value of the XSRF-TOKEN cookie from the document.cookie string and include it in the headers of the loginUser request.

CodePudding user response:

I just found out that axios decodes the xsrf-token and rtk query does not. The token includes a = sign at the end which is encoded to =.

Now I just had to decode the token with decodeURIComponent after reading it from the document and it did the trick.

Source: https://github.com/laravel/framework/discussions/42729#discussioncomment-2939906

  • Related