Here is how it worked for me:
in kernel.php
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:5000,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Session\Middleware\StartSession::class
],
in config/sanctum.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
))),
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. If this value is null, personal access tokens do
| not expire. This won't tweak the lifetime of first-party sessions.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
],
];
in .env (I use subdomains for both the main API and SPA website)
SESSION_DRIVER=cookie
SESSION_LIFETIME=86400
SESSION_DOMAIN=".myapidomain.com"
SANCTUM_STATEFUL_DOMAINS=".myapidomain.com"
in cors.php
'paths' => ['api/*','sanctum/csrf-cookie'],
'supports_credentials' => true,
in the user model ex: App\User
use Laravel\Sanctum\HasApiTokens;
i did't use vuejs on frontend so i used Laravel HTTP Facade, so i created a class for managing the SPA requests for both authenticated requests and non authenticated requests
<?php
namespace App\Http\Controllers\Api\Settings;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Session;
class Master
{
public $domainApiLink = "https://subdomain.mydomain/api/";
public $domainWebLink = "https://sbdomain.mydomain.com/";
private $cookies;
public function __construct(){
$this->generate_cookies();
}
public function generate_cookies(){
if(empty(Session::get('cookies'))) {
$cookies = Http::acceptJson()->asJson()->get($this->domainWebLink . 'sanctum/csrf-cookie')->throw()->cookies();
Session::put('cookies', $cookies);
}
}
public function getRequest($get_link_part, $type = '')
{
$activeLocale = app()->getLocale();
if(Session::has('auth_token') ) {
$response = Http::acceptJson()->asJson()
->withToken( Session::get('auth_token'))
->withOptions([
//'debug' => true
])
->get($this->domainApiLink . $activeLocale . $get_link_part)
->throw();
}else{
$response = Http::acceptJson()->asJson()
->withToken($this->website_token)
->withOptions([
//'debug' => true,
'cookies' => Session::get('cookies'),
])
->get($this->domainApiLink . $activeLocale . $get_link_part)
->throw();
}
switch ($type) {
case 'json':
$response = $response->json();
break;
case 'assoc':
if (is_array($response->json())) {
return $response->json();
} else {
$response = json_decode($response->json(), true);
if (empty($response)) {
return ['error' => 'Invalid Json response'];
}
}
break;
case 'object':
$response = $response->object();
break;
default:
$response = $response->body();
break;
}
return $response;
}
public function postRequest($inputs, $get_link_part, $type = '')
{
$activeLocale = app()->getLocale();
if(Session::has('auth_token') ) {
$response = Http::acceptJson()->asJson()
->withToken(Session::get('auth_token'))
//->withOptions(['debug' => true])
->post($this->domainApiLink . $activeLocale . $get_link_part, $inputs)
->throw();
}
else{
$response = Http::acceptJson()->asJson()
->withToken($this->website_token)
->withOptions([
//'debug' => true,
])
->post($this->domainApiLink. $activeLocale . $get_link_part, $inputs)
->throw();
}
switch ($type) {
case 'json':
$result = $response->json();
break;
case 'assoc':
if (is_array($response->json())) {
$result = $response->json();
} else {
$result = json_decode($response->json(), true);
if (empty($result)) {
$result = ['error' => 'Invalid Json response'];
}
}
break;
case 'object':
$result = $response->object();
break;
default:
$result = $response->body();
break;
}
return $result;
}
/**
* @return mixed
*/
public function getCookies()
{
return $this->cookies;
}
/**
* @param mixed $cookie
*/
public function setCookies($cookie): void
{
$this->cookies = $cookie;
}
}
i hope this helps you and i am happy to explain more, if needed