React + Laravel + Sanctum for api token authentication(NOT cookie)

6.2k views Asked by At

I am trying to get React and Laravel to work together using the middleware Sanctum.

I can read many examples of people trying to do this with a cookie based setup, but I am trying to use the token setup for a pure API approach. I am doing this because I want to prepare the backend for use in a mobile app where cookies are not available.

This is a part of my setup:

/backend/routes/api.php:

Route::post('/login', [ UserController::class, 'getAccessToken'] );

/frontend/store/api.js

static login( user ) {

    var formData = new FormData();
    formData.append('username', user.username);
    formData.append('password', user.password )
    formData.append('deviceName', 'browser');

    return fetch( 
        'http://localhost:5001/api/login, {
            method : 'post',
            body : formData
        }
    );
}

My problems is that it forces a CSRF token check, when the login route is accessed. That is even if the login route shouldn't be guarded by Sanctum. This of course fails when I am logging in and don't yet have a token to attach to the request. As I understand the token is only needed on accessing guarded routes after login. I have double checked that it is accessing the right route by renaming it to something fake and getting an error.

Am I doing something wrong with the use of Sanctum or is Sanctum just not the preferred use for api tokens? Should I maybe look into JWT instead?

Thank you in advance for your help. <3

3

There are 3 answers

0
Maya Kathrine Andersen On

In:

/backend/app/Http/Kernel.php

I had added:

\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,

It worked when I removed that line. I had failed to understand that a SPA equals the use of cookies, because I would say that I am also working on a SPA that just uses API tokens instead.

Next issue is that I am using Doctrine instead of Eloquent, which I can now see it far from compatible with Sanctum when it comes to issuing the token. But that will be a topic for another question.

0
Mihail Cojocaru On

Please, check this url, I was able to make it work thanks to this tutorial.

https://laravel-news.com/using-sanctum-to-authenticate-a-react-spa

here is my LoginForm.jsx

import React from "react";
import apiClient from "./services/apiClient";

const LoginForm = (props) => {
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    apiClient.get("/sanctum/csrf-cookie").then((response) => {
      apiClient
        .post("/api/sanctum-token", {
          email: email,
          password: password,
          device_name: "React v0.1",
        })
        .then((response) => {
          console.log(response);
        });
    });
  };
  return (
    <div>
      <h1>Login</h1>
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          name="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
        <input
          type="password"
          name="password"
          placeholder="Password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
        <button type="submit">Login</button>
      </form>
    </div>
  );
};

export default LoginForm;

apiClient.js

import axios from "axios";

const apiClient = axios.create({
  baseURL: "http://localhost",
  withCredentials: true,
});

export default apiClient;

0
Bonn On

I use this tutorial https://medium.com/@suton.tamimy/basic-token-authentication-with-fresh-laravel-8-sanctum-and-reactjs-ef14eba7ce0f

default setting laravel (no setting modifications etc) with API Login output TOKEN and BEARER token type. Then in ReactJs, just use this axios setting to generate the token (no need sanctum/csrf-cookie).

axios.defaults.baseURL = '_MY_API_';
    axios.post('/login', {
      email: '_EMAIL_INPUT_FORM _',
      password: '_PASSWORD_INPUT_FORM_'
    })
    .then(({ data }) => {
      if(data.status==="success"){
        console.log(data)
      } else {
        console.log("error")
      }
    });
  });