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).
2
Answers
Turns out that axios decodes the xsrf-token and rtk query does not. The token includes an = sign at the end which is encoded to %3D.
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
I got stuck on this for quite a while so I thought I would share what I did more explicitly.
RTK Query with
credentials: "include"
set will set the cookie on the document of the page. We can gather this cookie in thetransformResponse
ofinitCSRF
and put it in our state. Then we can useprepareHeaders
to grab our state and set theX-XSRF-TOKEN
for all our api responses.