Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Deadly_Smile's avatar

419 error when attempting to request from react to laravel

Front-End part

I have a react app that has this login form that sends this request

const handleLogin = async (e) => {
    e.preventDefault();
    try {
      await axios.post("/login", { email, password });
    } catch (e) {
      // console.log(e);
    }
  };

Note: This is my axios configure

import axios from "axios";

export default axios.create({
  baseURL: import.meta.env.VITE_BACKEND_URL,
  withCredentials: true,
});

Back-End part

.env:

APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:5173
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:5173

Note: I installed breeze with starter kit for API and I have these auth routes

Route::post('/register', [RegisteredUserController::class, 'store'])
                ->middleware('guest')
                ->name('register');

Route::post('/login', [AuthenticatedSessionController::class, 'store'])
                ->middleware('guest')
                ->name('login');
// and some more

I also configured kernel.php like this -

'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\VerifyCsrfToken::class,

        ],

Main problem

I am getting 419 error and this error response -

"message": "CSRF token mismatch.",
"exception": "Symfony\Component\HttpKernel\Exception\HttpException",
"line": 492,

Can anyone tell me what am I doing wrong and how can I solve this issue ? Thanks in advance.

0 likes
5 replies
Deadly_Smile's avatar

@tykus I tried this way now and still getting same error

const handleLogin = async (e) => {
    e.preventDefault();
    axios.get("/sanctum/csrf-cookie").then(async (response) => {
      // Login...
      await axios.post("/login", { email, password });
    });
    // try {
    //   await axios.post("/login", { email, password });
    // } catch (e) {
    //   // console.log(e);
    // }
  };

I was getting tokens anyway, this is the SS

React part - https://ibb.co/1GQby46

Laravel Server part - https://ibb.co/dK36WHT

1 like
philipebarra's avatar
Level 7

Since axios's version 1.6.0 you need to use withXSRFToken as described here https://github.com/axios/axios/pull/6046:

import axios from "axios";

export default axios.create({
  baseURL: import.meta.env.VITE_BACKEND_URL,
  withCredentials: true,
  withXSRFToken: true
});

Don't forget to change config/cors.php file:

'supports_credentials' => true,//<-- default is false

And update this file if you are using sanctum with SPA authentication: app/Http/kernel.php

'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,//<-- remove comment from this line
...
];
1 like
Deadly_Smile's avatar

@philipebarra thanks your approach worked but when i try with RTK(react toolkit) query it gives 419 error. Is there a way to implement this with RTK(react toolkit) query ?

This was my approach -

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import axios from "axios";

const getCsrfTokenFromState = async (state) => {
  try {
    const response = await axios.get(
      `${import.meta.env.VITE_BACKEND_URL}/sanctum/csrf-cookie`
    );

    if (response.status === 204) {
      return state.csrfToken;
    }
  } catch (error) {
    throw new Error("Failed to fetch CSRF token");
  }
};

const UsersAPI = createApi({
  reducerPath: "users",

  baseQuery: fetchBaseQuery({
    baseUrl: `${import.meta.env.VITE_BACKEND_URL}/api`,
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
    },
  }),
  endpoints(builder) {
    return {
      getUser: builder.query({
        providesTags: (result, error, arg) => {
          const tags = [{ type: "user" }];
          return tags;
        },
        query: () => "/user",
      }),
      login: builder.mutation({
        transformHeaders: async (headers, { getState }) => {
          const csrfToken = await getCsrfTokenFromState(getState());
          console.log(csrfToken);
          headers.set("X-CSRF-TOKEN", csrfToken);
          return headers;
        },
        query: ({ email, password }) => {
          return {
            url: "/login",
            body: { email, password },
            method: "POST",
          };
        },
        invalidatesTags: [{ type: "user" }],
      }),
      signup: builder.mutation({
        invalidatesTags: (result, error, arg) => {
          return [{ type: "user" }];
        },
        query: ({ email, password, confirmPassword, name }) => {
          return {
            url: "/register",
            body: {
              name,
              email,
              password,
              password_confirmation: confirmPassword,
            },
            method: "POST",
          };
        },
      }),
    };
  },
});

export const { useGetUserQuery, useLoginMutation, useSignupMutation } =
  UsersAPI;
export { UsersAPI };

Deadly_Smile's avatar

My approach

In RTK (React Toolkit) query, the token needs decoding as it includes "%3d" at the end. When using standalone axios, retrieve the exact token without any additional parts. This was the issue in my case that axios gets the job done, but using RTK (React Toolkit) resulted in a 419 error.

Solution

I added header like this with each query :

endpoints(builder) {
    return {
      initCsrf: builder.query({
        query() {
          return {
            url: "sanctum/csrf-cookie",
            credentials: "include",
            headers: {
              "X-XSRF-TOKEN": decodeURIComponent(getCookie("XSRF-TOKEN")),
              "X-Requested-With": "XMLHttpRequest",
              "Content-Type": "application/json",
              Accept: "application/json",
            },
          };
        },
      }),
      login: builder.mutation({
        query: ({ email, password }) => {
          return {
            url: "api/login",
            body: { email, password },
            method: "POST",
            credentials: "include",
            headers: {
              "X-XSRF-TOKEN": decodeURIComponent(getCookie("XSRF-TOKEN")),
              "X-Requested-With": "XMLHttpRequest",
              "Content-Type": "application/json",
              Accept: "application/json",
            },
          };
        },
        invalidatesTags: [{ type: "user" }],
      }),
      signup: builder.mutation({
        invalidatesTags: (result, error, arg) => {
          return [{ type: "user" }];
        },
        query: ({ email, password, confirmPassword, name }) => {
          return {
            url: "api/register",
            body: {
              name,
              email,
              password,
              password_confirmation: confirmPassword,
            },
            method: "POST",
            headers: {
              "X-XSRF-TOKEN": decodeURIComponent(getCookie("XSRF-TOKEN")),
              "X-Requested-With": "XMLHttpRequest",
              "Content-Type": "application/json",
              Accept: "application/json",
            },
          };
        },
      }),
    };
  },

The getCookie function :

const getCookie = (cookieName) => {
  const cookieArray = document.cookie.split(";");

  for (const cookie of cookieArray) {
    let cookieString = cookie;

    while (cookieString.charAt(0) == " ") {
      cookieString = cookieString.substring(1, cookieString.length);
    }
    if (cookieString.indexOf(cookieName + "=") == 0) {
      return cookieString.substring(cookieName.length + 1, cookieString.length);
    }
  }

  return undefined;
};

Conclusion: It worked for me.

Please or to participate in this conversation.