0
votes

I'm using Laravel 8 and I've been trying to follow the sanctum documentation for SPA authentication. I've already done setting up the necessary configurations required. The backend server is running on localhost with default port(80) while SPA client is running on localhost:3000. I'm using nuxt framework for client with axios to make a request.

The initial request should be made to /sanctum/csrf-cookie to initialize the CSRF protection cookie and here's what network traffic traffic shows

enter image description here

The second request is the actual request that should contain the cookies sent by the first request for the domain but it looks like the XSRF-TOKEN is being skipped. Here's what network traffic looks like

enter image description here

sanctum.php config file:

<?php
return [
    'stateful' => explode(',', env(
        'SANCTUM_STATEFUL_DOMAINS',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
    )),
    'expiration' => null,
    'middleware' => [
        'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
        'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
    ],
];

cors.php config file:

<?php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*', 'localhost:3000'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => ['XSRF-TOKEN', 'X-XSRF-TOKEN'],
    'max_age' => 0,
    'supports_credentials' => true,
];

session.php config file

<?php

use Illuminate\Support\Str;

return [
    'driver' => env('SESSION_DRIVER', 'file'),
    'lifetime' => env('SESSION_LIFETIME', 120),
    'expire_on_close' => false,
    'encrypt' => false,
    'files' => storage_path('framework/sessions'),
    'connection' => env('SESSION_CONNECTION', null),
    'table' => 'sessions',
    'store' => env('SESSION_STORE', null),
    'lottery' => [2, 100],
    'cookie' => env(
        'SESSION_COOKIE',
        Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
    ),
    'path' => '/',
    'domain' => env('SESSION_DOMAIN', null),
    'secure' => env('SESSION_SECURE_COOKIE'),
    'http_only' => true,
    'same_site' => 'lax',
];

I set this in nuxt.config.js

export default {
  axios: {
    withCredentials: true,
    baseURL: 'http://localhost/',
  },
}

Can somebody tell me why the XSRF-TOKEN cookie is not getting sent back?

Thanks

3
No replies so far, can you share some more info? cors.php and session.php would be helpful. - Nick Dawes
@NickDawes Updated now. - Michael Erwin

3 Answers

2
votes

Turns out the problem was caused by nuxt configuration for axios module. I just replaced the field 'withCredentials' with simply 'credentials'.

This is now the updated nuxt.config.js:

axios: {
    credentials: true,
    baseURL: 'http://localhost/api/',
  },
0
votes

Here's my Nuxt/Laravel 8/Sanctum config.

Notice I've not included 'sanctum/csrf-cookie' in 'paths', because I've changed the prefix for Sanctum in sanctum.php. I've also got my nuxt dev server on port 3050, which is what you'll see in my config. Change yours to suit (e.g. 3000).

cors.php

'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => [env('ALLOWED_ORIGINS')], 
    .env e.g.: ALLOWED_ORIGINS=http://localhost:3050
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,

Everything in session.php is left default, but make sure you update your .env with the correct SESSION_DOMAIN.

session.php

'driver' => env('SESSION_DRIVER', 'file'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => false,
'files' => storage_path('framework/sessions'),
'connection' => env('SESSION_CONNECTION', null),
'table' => 'sessions',
'store' => env('SESSION_STORE', null),
'lottery' => [2, 100],
'cookie' => env(
    'SESSION_COOKIE',
    Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
'path' => '/',
'domain' => env('SESSION_DOMAIN', null),
    .env e.g.: SESSION_DOMAIN=localhost
'secure' => env('SESSION_SECURE_COOKIE'),
'http_only' => true,
'same_site' => 'lax',

Everything in sanctum.php is default, make sure you enter the correct SANCTUM_STATEFUL_DOMAIN in your .env (example below). I've also added the line 'prefix' => 'api'. This allows me to set a single baseURL in axios and call the csrf-cookie route via http://localhost:3050/api/csrf-cookie. See more about it here.

sanctum.php

'stateful' => explode(',', env(
    'SANCTUM_STATEFUL_DOMAINS',
        .env e.g.: SANCTUM_STATEFUL_DOMAINS=localhost:3050
    'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
)),
'expiration' => null,
'middleware' => [
    'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
    'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
],
'prefix' => 'api'

Then on the frontend, I have set axios up with a baseURL and ensured withCredentials is set to true

axios.defaults.withCredentials = true;
axios.defaults.baseURL = process.env.API_BASE_URL;
    .env e.g.: API_BASE_URL=http://localhost:8000/api

Assuming you've made no other changes that contradict the settings above, this should work :)

0
votes

If you followed these guides and you're still having problems, as I did, you might have missed setting SESSION_DOMAIN in your .env file.

Open Laravel's .env file and add SESSION_DOMAIN=.localhost

One more thing: If you cannot access "/csrf-cookie" route for some reason and still need to call "sanctum/csrf-cookie/", but Axios is adding /api in front of your requests, then use this piece of code:

let myNewInstance = this.$axios.create({
   baseURL: 'http://localhost' //without /api at the end
});

myNewInstance.get('/sanctum/csrf-cookie');