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

FrankClark's avatar

Laravel Sanctum - Token Based Authentication

There are a lot of discussions and tutorials for using sanctum with a SPA application. I am trying to use token based authentication similar to how you would achieve it with Laravel Passport. I am getting a fairly common problem with CORS complaining of a CSRF token mismatch which of course does not occur with Postman, but it does when i try to use Guzzle, or Laravel's HTTP facade.

Unlike implementations online, i do not create a token on login, but rather have a UI for creating tokens. So the user has logged in and created a token and that token is stored for use for 6 months.

Below is an example request from application-one.local to application-two.local (this code is called directly from a controller that i am using for testing purposes)

$response = Http::withToken('the token')
            ->withHeaders([
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ])->withOptions([
                'verify' => false,
            ])->patch('https://application-two.local/api/test');

        if ($response->failed()) {
            \Log::error('Failed to retrieve response from application 2!');
            abort(500);
        }

I know that the token is correct, because Postman can use this token and I get a valid response. Verify has been set to false because my local VM does not have a valid SSL certificate.

Application two, has CORS configuration that I have kept completely open for testing purposes :

return [
    'paths' => ['/*', 'api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];

The error :

Application Two API Request Failure (419) Client error: `PATCH https://application-two.local/api/test` resulted in a `419 unknown status` response:
{
    "message": "CSRF token mismatch.",
    "exception": "Symfony\Component\HttpKernel\Exception\HttpException",
   (truncated...)

Other information of note

  • I have tried commenting out \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class from the API section of middleware groups

  • I have tried changing my SESSION_DRIVER to 'cookie' from 'database'

  • Sanctum config is default, but i have tried to add additional domains to the stateful domains list :

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1,192.168.56.4,application-one.local',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),
  • my route in laravel (for application-two) looks like this :
Route::group(['prefix' => '/api', 'as' => 'api.', 'middleware' => ['auth:sanctum', 'verified']], function() {
    Route::patch('test', [TestController::class, 'test'])->name('test');
});
  • I have tried using http rather than https

I am sure there is something fundamental that I am not understanding but any help would be appreciated.

0 likes
2 replies
FrankClark's avatar
FrankClark
OP
Best Answer
Level 2

Ok, bit embarrassing. In my haste, i had just put my api routes into the web.php file - moving them to the api.php file solved the issues. Obviously it was applying the \App\Http\Middleware\VerifyCsrfToken::class middleware which is only required for web requests.

I'll keep this here incase someone else is stupid like me.

1 like
santosvilanculos's avatar

@FrankClark

Thank you so much! I had the same CSRF token mismatch after struggling with this bug for a while. Your suggestion about the Passport routes was the key. Using withRouting to define a route group in the bootstrap/app.php file, like this solved it immediately:

->withRouting(
    web: __DIR__.'/../routes/web.php',
    api: __DIR__.'/../routes/api.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
    then: function () {
        Route::group([
            'as' => 'passport.',
            'prefix' => config('passport.path', 'oauth'),
            'namespace' => 'Laravel\Passport\Http\Controllers',
        ], base_path('routes/passport.php'));
    },
)

Seriously, I can't thank you enough!

Please or to participate in this conversation.