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

Ashraam's avatar
Level 41

Sanctum SPA + api routes authentication does not work

Hi everyone,

I'm trying to build an SPA frontend with a laravel 8 backend using Sanctum for authentication.

I've been doing all the configuration following the official documentation.

I can log myself (using Fortify), the authentication using auth:sanctum work in the routes/web.php file, but how can I make it work on routes/api.php ?

I've tried using auth:sanctum or auth:api but I've always got an unauthorized response.

The only way to make it work (for now) is to change the middleware from api to web in the RouteServiceProvider

Did I miss something ?

Thanks

0 likes
8 replies
devingray_'s avatar

Hi. Can you post some code that we can have a look at? I think it will help us to further understand what you are trying to do.

Ashraam's avatar
Level 41

@devingray_ Sure but I don't what exactly to show you, it's really basic I guess. Here are the files I've edited on the backend

web.php

Route::get('/', function () {
    return view('welcome');
});

Route::middleware('auth:sanctum')->get('user', function () {
    return auth()->user();
});

api.php

Route::middleware('auth:api')->get('user-api', function () {
    return auth()->user();
});

Route::middleware('auth:sanctum')->get('user-sanctum', function () {
    return auth()->user();
});

cors.php


    'paths' => ['api/*', 'login', 'logout', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

Kernel.php

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

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

RouteServiceProvider.php

public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
        });
    }
devingray_'s avatar

And how are you issuing the sanctum tokens? You will need to have a sanctum token to be able to login to make the request to user-sanctum

This can be generated like this (Taken directly from the docs, https://laravel.com/docs/8.x/sanctum#spa-authentication)


    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device_name' => 'required',
    ]);

    $user = User::where('email', $request->email)->first();

    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }

    return $user->createToken($request->device_name)->plainTextToken;

Ashraam's avatar
Level 41

@devingray_ I'm using the default Fortify login route (so no token are created I guess)

I created a route to generate a token

Route::middleware('auth:sanctum')->get('token', function () {
    return auth()->user()->createToken('authToken')->plainTextToken;
});

it's creating a token, then I add it to the headers in axios, it's working now, but I've got an error when I try to logout

async getToken() {
            const res = await axios.get('token');

            this.token = res.data;

            console.log('token', this.token);

            axios.defaults.headers.common = {'Authorization': `Bearer ${this.token}`}
        },

        async logout() {
            axios.defaults.headers.common = {'Authorization': ''}

            const res = await axios.post('logout');

            console.log('LOGGED OUT', res.data);
        }
Access to XMLHttpRequest at 'http://backend.spa.test/' (redirected from 'http://localhost:8080/logout') from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

any idea ?

devingray_'s avatar
Level 8

Yeah the logout function is handled with fortify, you will need to make a new route for logout and in that Route simply delete the user token

$request->user()->currentAccessToken()->delete();
Ashraam's avatar
Level 41

Well I followed the docs and here is my logout function

public function logout()
    {
        if (auth()->check()) {
            auth()->user()->tokens()->delete();
    
            request()->session()->invalidate();
    
            request()->session()->regenerateToken();
    
            Auth::logout();
        }

        return response()->noContent();
    }

thanks @devingray_

1 like
realife's avatar

It's an old post I know but I just want to make clear that the proposed solution (while it may work) is not appropriate for SPA that you are accessing through a browser on the same top domain. In this case there is no need to manage tokens, as Sanctum uses Laravel's built-in cookie based session authentication services. Tokens are only needed if you want to authenticate a mobile application for example, as the docs clearly state.

Please or to participate in this conversation.