Cos’è Sanctum
Laravel Sanctum è un pacchetto di autenticazione leggero per SPA, applicazioni mobili e API semplici. Senza conoscenze approfondite di OAuth puoi emettere e gestire più API token per utente.
Sanctum risolve due problemi.
Modalità Meccanismo Uso Autenticazione API token Header Authorization: Bearer <token> App mobili, integrazioni di terze parti Autenticazione SPA Cookie di sessione + protezione CSRF Frontend proprie (Vue/React, ecc.)
Per API chiamate dalla tua SPA usa l’autenticazione SPA. Per app mobili o integrazioni terze usa l’autenticazione con API token. Puoi usarne solo una.
Quando usare Passport
Sanctum Passport Complessità Semplice OAuth2 completo Uso SPA/app mobili proprie Provider OAuth per app esterne Tipo di token Personal access token OAuth2 access token
Se devi essere un provider OAuth2 per servizi esterni, scegli Passport; per il resto Sanctum è sufficiente.
Installazione e configurazione
Installazione
Installa laravel/sanctum, pubblica la migration di personal_access_tokens ed esegue le migration.
Trait HasApiTokens
Aggiungi il trait a User.
use Laravel\Sanctum\ HasApiTokens ;
class User extends Authenticatable
{
use HasApiTokens , HasFactory , Notifiable ;
}
Ora hai $user->createToken() e $user->tokens.
Autenticazione con API token
Flusso
Emettere un token
createToken() restituisce plainTextToken. Il token in chiaro non viene salvato : restituiscilo subito al client.
use Illuminate\Http\ Request ;
Route :: post ( '/tokens/create' , function ( Request $request ) {
$token = $request -> user () -> createToken ( $request -> token_name );
return [ 'token' => $token -> plainTextToken ];
}) -> middleware ( 'auth' );
Nel DB è salvato con hash SHA-256.
Scope (abilities)
$token = $user -> createToken ( 'mobile-app' , [ 'server:update' , 'server:read' ]);
return $token -> plainTextToken ;
Verifica gli scope:
if ( $request -> user () -> tokenCan ( 'server:update' )) {
// Aggiorna
}
if ( $request -> user () -> tokenCant ( 'server:update' )) {
abort ( 403 );
}
Middleware per scope
In bootstrap/app.php:
use Laravel\Sanctum\Http\Middleware\ CheckAbilities ;
use Laravel\Sanctum\Http\Middleware\ CheckForAnyAbility ;
-> withMiddleware ( function ( Middleware $middleware ) : void {
$middleware -> alias ([
'abilities' => CheckAbilities :: class ,
'ability' => CheckForAnyAbility :: class ,
]);
})
Route :: get ( '/orders' , function () {
// ...
}) -> middleware ([ 'auth:sanctum' , 'abilities:check-status,place-orders' ]);
Route :: get ( '/orders' , function () {
// ...
}) -> middleware ([ 'auth:sanctum' , 'ability:check-status,place-orders' ]);
Scadenza dei token
Di default nessuna scadenza. In config/sanctum.php expiration in minuti.
'expiration' => 525600 , // 365 giorni
Per token:
$token = $user -> createToken (
'token-name' ,
[ '*' ],
now () -> addWeeks ( 1 )
) -> plainTextToken ;
Prune periodico:
use Illuminate\Support\Facades\ Schedule ;
Schedule :: command ( 'sanctum:prune-expired --hours=24' ) -> daily ();
Revocare token
$user -> tokens () -> delete ();
$request -> user () -> currentAccessToken () -> delete ();
$user -> tokens () -> where ( 'id' , $tokenId ) -> delete ();
Autenticazione SPA
Con cookie di sessione, senza gestione token. Ideale per frontend proprie.
SPA e API devono condividere lo stesso dominio (i sottodomini possono differire). Le richieste devono includere Accept: application/json e Referer o Origin.
Attiva il middleware Sanctum
In bootstrap/app.php:
-> withMiddleware ( function ( Middleware $middleware ) : void {
$middleware -> statefulApi ();
})
Domini first-party
'stateful' => explode ( ',' , env ( 'SANCTUM_STATEFUL_DOMAINS' , sprintf (
'%s%s' ,
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1' ,
Sanctum :: currentApplicationUrlWithPort ()
))),
CORS
php artisan config:publish cors
'supports_credentials' => true ,
Frontend axios:
axios . defaults . withCredentials = true ;
axios . defaults . withXSRFToken = true ;
Sessione cookie:
'domain' => '.example.com' ,
Flusso di login SPA
Ottieni il cookie CSRF
await axios . get ( '/sanctum/csrf-cookie' );
Richieste autenticate
const response = await axios . get ( '/api/user' );
console . log ( response . data );
Protezione delle route
Applica auth:sanctum.
use Illuminate\Http\ Request ;
Route :: get ( '/user' , function ( Request $request ) {
return $request -> user ();
}) -> middleware ( 'auth:sanctum' );
Route :: middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: get ( '/profile' , [ ProfileController :: class , 'show' ]);
Route :: put ( '/profile' , [ ProfileController :: class , 'update' ]);
Route :: get ( '/posts' , [ PostController :: class , 'index' ]);
});
Esempio: API di login e restituzione del token
Endpoint di login
use App\Models\ User ;
use Illuminate\Http\ Request ;
use Illuminate\Support\Facades\ Hash ;
use Illuminate\Validation\ ValidationException ;
Route :: post ( '/sanctum/token' , function ( Request $request ) {
$request -> validate ([
'email' => [ 'required' , 'email' ],
'password' => [ 'required' ],
'device_name' => [ 'required' , 'string' ],
]);
$user = User :: where ( 'email' , $request -> email ) -> first ();
if ( ! $user || ! Hash :: check ( $request -> password , $user -> password )) {
throw ValidationException :: withMessages ([
'email' => [ 'Credenziali non valide.' ],
]);
}
return response () -> json ([
'token' => $user -> createToken ( $request -> device_name ) -> plainTextToken ,
]);
});
Route autenticate
Route :: middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: get ( '/user' , function ( Request $request ) {
return $request -> user ();
});
Route :: post ( '/logout' , function ( Request $request ) {
$request -> user () -> currentAccessToken () -> delete ();
return response () -> json ([ 'message' => 'Logout effettuato.' ]);
});
Route :: post ( '/logout/all' , function ( Request $request ) {
$request -> user () -> tokens () -> delete ();
return response () -> json ([ 'message' => 'Logout da tutti i dispositivi.' ]);
});
});
Chiamate dal client
const { data } = await axios . post ( '/api/sanctum/token' , {
email: '[email protected] ' ,
password: 'password' ,
device_name: 'My iPhone' ,
});
const token = data . token ;
const response = await axios . get ( '/api/user' , {
headers: {
Authorization: `Bearer ${ token } ` ,
},
});
Test
Con Sanctum::actingAs() autentichi e definisci gli scope.
use App\Models\ User ;
use Laravel\Sanctum\ Sanctum ;
test ( 'è possibile ottenere la lista dei task' , function () {
Sanctum :: actingAs (
User :: factory () -> create (),
[ 'view-tasks' ]
);
$response = $this -> get ( '/api/tasks' );
$response -> assertOk ();
});
test ( 'token con tutte le abilities' , function () {
Sanctum :: actingAs (
User :: factory () -> create (),
[ '*' ]
);
$response = $this -> get ( '/api/tasks' );
$response -> assertOk ();
});
use App\Models\ User ;
use Laravel\Sanctum\ Sanctum ;
public function test_task_list_can_be_retrieved () : void
{
Sanctum :: actingAs (
User :: factory () -> create (),
[ 'view-tasks' ]
);
$response = $this -> get ( '/api/tasks' );
$response -> assertOk ();
}
Riepilogo
use Laravel\Sanctum\ HasApiTokens ;
class User extends Authenticatable
{
use HasApiTokens , HasFactory , Notifiable ;
}
$token = $user -> createToken ( 'token-name' ) -> plainTextToken ;
$token = $user -> createToken ( 'token-name' , [ 'read' , 'write' ]) -> plainTextToken ;
$user -> tokenCan ( 'read' );
$user -> tokenCant ( 'write' );
$user -> tokens () -> delete ();
$request -> user () -> currentAccessToken () -> delete ();
$user -> tokens () -> where ( 'id' , $id ) -> delete ();
API token : client senza sessione (mobile, terze parti, CLI).
SPA : frontend Vue/React/Next.js sullo stesso dominio. Più sicura, no token da gestire.