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

minthant's avatar

Laravel Sanctum SPA authentication 401 error with reactjs and laravel api and API authentication

Hi. I am trying to use Laravel Sanctum both for API authentication and SPA authentication. In SPA authentication. Do I need to define sanctum/csrf-cookie in api.php? If so, which one would be the correct one? 1 or 2? I got 204 error if I use 1. I got 401 error if I use 2.

Route::get('/sanctum/csrf-cookie', [CsrfCookieController::class, 'show']);
Route::middleware('auth:sanctum')->get('/sanctum/csrf-cookie', function (Request $request) { return response()->json(['message' => 'CSRF cookie set']); });

I have uncommented the following line as well.

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

bootstrap.js file

import axios from 'axios';
window.axios = axios;
axios.defaults.withCredentials = true;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

.env file

SANCTUM_STATEFUL_DOMAINS=http://localhost:3000
SESSION_DOMAIN=localhost
APP_URL=http://localhost:3000

api.php

Route::post('/registration', [UserController::class, 'registration']);
Route::post('/signin', [UserController::class, 'signin']);
Route::middleware('auth:sanctum')->post('/signout', [UserController::class, 'signout']);

The registration function from UserController

public function registration(Request $request) {
        $credentials = $request->validate([
            'name' => 'required',
            'email' => 'required',
            'password' => 'required'
        ]);
        
        // Check if the email is already taken
        $existinguser = User::where('email', $credentials['email'])->first();
        if($existinguser) {
            return response()->json(['error'=>'Email already in use'], 400);
        }
        
        $user = new User;
        $user->name = $credentials['name'];
        $user->email = $credentials['email'];
        $user->password = bcrypt($credentials['password']);
        $user->save();
        return response()->json(['message'=>'User created successfully']);
}

signin function in UserController

public function signin(Request $request) {
        $credentials = $request->validate([
            'email' => 'required',
            'password' => 'required'
        ]);
    
        if (Auth::attempt($credentials)) {
            $user = User::where('email', $credentials['email'])->first();
            if ($user && Hash::check($credentials['password'], $user->password)) {
                $token = $user->createToken('api-token')->plainTextToken;
                return response()->json(['user' => $user, 'token' => $token]);
            }
        }
        return response()->json(['error' => 'Email or password is incorrect'], 401);
}

I received the token in the response after signin.

signout function

public function signout(Request $request) {

        $request->user()->tokens()->delete();

        return response()->json(['message' => 'Logged Out'], 200);
}

To use the token I receive from signin function as Authorization Bearer, do I store in localStorage? I read somewhere that it is not safe and not a good practice to store the token the token in localStorage. I received the token in the response.

The following is signin.js file

if(email && password) {
      let item={email, password}
      try {
          await axios.get('http://127.0.0.1:8000/api/sanctum/csrf-cookie')
          const response = await axios.post('http://127.0.0.1:8000/api/signin', item, {
            withCredentials: true,
          })
        
        if(response.status === 200) {
          console.log(response)
          localStorage.setItem('user_info', response.data.user.name)
          localStorage.setItem('api_token', response.data.token)
          router.push('/')
        } else {
          setsigninError('Error registering', error)
          console.log('error A')
        }
      } catch(error) {
          setsigninError(error.response.data.error);
          console.log(error.response.data.error)
      }
}

Is my API authentication using Laravel Sanctum correct?

Lets say I save the token I got from the signin function in the localStorage, I need to use that token in signin request as Authorization Bearer? How can I do that if I get the token after signin request. I really don't understand this.

This is the route I am redirecting after signin.

Route::get('/', [ProductController::class, 'showproduct']);

Please help.

0 likes
0 replies

Please or to participate in this conversation.