📍 Guide de référence de cette série : Laravel 11 et PHP 8.4 : installer l’environnement et maîtriser la nouvelle architecture
L’authentification est la couche de sécurité la plus critique de toute application web. Laravel 11 propose Laravel Sanctum pour les APIs et les SPAs — une solution légère qui émet des tokens API portables ou s’appuie sur les cookies de session pour les applications JavaScript single-page. Ce tutoriel couvre les deux modes d’utilisation de Sanctum : les tokens API pour les clients mobiles et les services tiers, et l’authentification SPA pour les frontends Vue.js, React ou Nuxt servis sur le même domaine ou un sous-domaine.
Prérequis
- Projet Laravel 11 — guide d’installation
- API REST en place — tutoriel API REST Laravel 11
- Table
usersavec les migrations par défaut Laravel - Temps estimé : 45 minutes
Étape 1 — Installer Sanctum et préparer la base
Dans Laravel 11, Sanctum est installé via la commande install:api qui configure en même temps le routing API. Si vous avez déjà exécuté cette commande lors de la création de votre API, Sanctum est déjà installé. Dans le cas contraire, lancez-la maintenant :
php artisan install:api
Cette commande installe le package laravel/sanctum via Composer, publie le fichier de configuration config/sanctum.php, publie et exécute la migration qui crée la table personal_access_tokens. Cette table stocke tous les tokens émis : leur nom, le hash SHA-256 de leur valeur, les abilities (permissions) associées, et la date d’expiration optionnelle. Vérifiez que la migration a bien créé la table :
php artisan migrate:status
Vous devez voir create_personal_access_tokens_table avec le statut Ran. Si ce n’est pas le cas, relancez php artisan migrate.
Étape 2 — Configurer le modèle User avec HasApiTokens
Pour qu’un modèle puisse émettre et valider des tokens Sanctum, il doit utiliser le trait HasApiTokens. Ce trait ajoute les méthodes createToken(), tokens(), tokenCan() et currentAccessToken() au modèle. Ouvrez app/Models/User.php et ajoutez le trait :
<?php
// app/Models/User.php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed', // Hash automatique via cast (Laravel 10+)
];
}
Le cast 'password' => 'hashed' est une fonctionnalité introduite dans Laravel 10 qui hash automatiquement le mot de passe lors de l’affectation — plus besoin de bcrypt() ou Hash::make() manuel dans les contrôleurs. Assurez-vous que ce cast est présent dans votre modèle User.
Étape 3 — Créer le contrôleur d’authentification
Le flux d’authentification par token Sanctum comporte trois opérations : l’inscription (register), la connexion (login) et la déconnexion (logout). Créez un contrôleur dédié :
php artisan make:controller Auth/AuthController
<?php
// app/Http/Controllers/Auth/AuthController.php
namespace AppHttpControllersAuth;
use AppHttpControllersController;
use AppModelsUser;
use IlluminateHttpJsonResponse;
use IlluminateHttpRequest;
use IlluminateSupportFacadesAuth;
use IlluminateValidationValidationException;
class AuthController extends Controller
{
// POST /api/register
public function register(Request $request): JsonResponse
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
$user = User::create($validated);
$token = $user->createToken('api-token')->plainTextToken;
return response()->json([
'user' => $user,
'token' => $token,
], 201);
}
// POST /api/login
public function login(Request $request): JsonResponse
{
$request->validate([
'email' => ['required', 'email'],
'password' => ['required', 'string'],
'device_name' => ['required', 'string', 'max:100'],
]);
if (! Auth::attempt($request->only('email', 'password'))) {
throw ValidationException::withMessages([
'email' => ['Les identifiants fournis sont incorrects.'],
]);
}
$user = Auth::user();
$token = $user->createToken($request->device_name)->plainTextToken;
return response()->json(['token' => $token]);
}
// POST /api/logout
public function logout(Request $request): JsonResponse
{
// Révoquer uniquement le token courant (pas tous les tokens)
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Déconnecté avec succès.']);
}
}
La méthode login demande un champ device_name — c’est la bonne pratique Sanctum pour nommer les tokens par appareil ("iPhone 15 de Moussa", "Chrome Windows", etc.). Cela permet à l’utilisateur de révoquer les tokens par appareil depuis son espace personnel. Auth::attempt() vérifie les identifiants ; s’ils sont invalides, on lance une ValidationException qui Laravel transforme automatiquement en réponse JSON 422 avec le champ d’erreur.
Étape 4 — Déclarer les routes d’authentification
Ouvrez routes/api.php et ajoutez les routes publiques (inscription, connexion) et la route protégée (déconnexion). La route /user générée par défaut par Sanctum peut servir à vérifier le token courant :
<?php
// routes/api.php
use AppHttpControllersAuthAuthController;
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
// Routes publiques — accessibles sans token
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
// Routes protégées — requièrent un token Sanctum valide
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/user', fn (Request $request) => $request->user());
});
Étape 5 — Tester les endpoints d’authentification
Démarrez le serveur de développement (php artisan serve) et testez le flux complet avec curl. Remplacez les valeurs par vos propres données :
# 1. Inscription
curl -s -X POST http://localhost:8000/api/register -H "Content-Type: application/json" -H "Accept: application/json" -d '{"name":"Mamadou Diallo","email":"mamadou@example.com","password":"secret12","password_confirmation":"secret12"}' | jq .
# Réponse attendue : {"user":{...}, "token":"1|AbCdEfGhIj..."}
# 2. Connexion et récupération du token
TOKEN=$(curl -s -X POST http://localhost:8000/api/login -H "Content-Type: application/json" -H "Accept: application/json" -d '{"email":"mamadou@example.com","password":"secret12","device_name":"curl-test"}' | jq -r '.token')
echo "Token: $TOKEN"
# 3. Accéder à une route protégée
curl -s http://localhost:8000/api/user -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" | jq .
Le signal de succès : la troisième commande retourne le JSON de l’utilisateur connecté ({"id":1,"name":"Mamadou Diallo","email":"..."}). Si vous obtenez {"message":"Unauthenticated."}, vérifiez que le header Authorization: Bearer TOKEN est bien envoyé et que le middleware auth:sanctum est appliqué à la route.
Étape 6 — Token abilities : permissions granulaires
Sanctum permet d’associer des abilities (permissions) à chaque token émis. Cela permet de créer des tokens avec des droits limités — utile pour les intégrations tierces ou les tokens d’accès à usage unique. Voici comment émettre un token avec abilities et vérifier ces permissions dans un contrôleur :
<?php
// Émettre un token avec abilities spécifiques
$token = $user->createToken('mobile-app', ['articles:read', 'articles:create'])->plainTextToken;
// Vérifier les abilities dans un contrôleur ou middleware
public function store(Request $request): JsonResponse
{
if (! $request->user()->tokenCan('articles:create')) {
return response()->json(['error' => 'Permission insuffisante.'], 403);
}
// ...
}
// Émettre un token avec expiration (ex: 24h)
$token = $user->createToken(
'temp-access',
['reports:read'],
now()->addHours(24)
)->plainTextToken;
// Planifier la suppression des tokens expirés (routes/console.php)
Schedule::command('sanctum:prune-expired --hours=24')->daily();
La commande sanctum:prune-expired supprime de la table personal_access_tokens les tokens dont la date d’expiration est dépassée. Sans cette commande planifiée, la table grossit indéfiniment. Ajoutez-la dans routes/console.php avec une fréquence quotidienne.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
Unauthenticated. malgré un token valide |
Header Accept: application/json absent → Laravel retourne une redirection HTML au lieu d’un JSON 401 |
Toujours envoyer Accept: application/json dans les requêtes API |
| Token présent mais abilities incorrectes | Token créé sans abilities ou avec un nom incorrect | Utiliser tokenCan('ability-name') et vérifier les abilities lors de l’émission |
Table personal_access_tokens absente |
php artisan install:api non exécuté ou migration non lancée |
Lancer php artisan migrate après install:api |
| Mot de passe non hashé en base | Cast 'password' => 'hashed' absent du modèle User |
Ajouter le cast ou utiliser Hash::make() explicitement |
FAQ
Quelle différence entre Sanctum et Passport ?
Passport implémente le protocole OAuth2 complet (authorization code, client credentials, etc.) — nécessaire pour des intégrations tiers complexes comme « Se connecter avec MonApp ». Sanctum est plus simple et couvre 90% des besoins : tokens API et SPA sur le même domaine. Choisissez Passport uniquement si vous avez besoin d’OAuth2 complet.
Comment révoquer tous les tokens d’un utilisateur ?
$user->tokens()->delete() révoque tous les tokens. Pour révoquer seulement le token courant : $request->user()->currentAccessToken()->delete().
Peut-on limiter le nombre de tokens actifs par utilisateur ?
Pas nativement dans Sanctum. Il faut implémenter cette logique manuellement : avant d’émettre un nouveau token, compter les tokens existants et en supprimer les plus anciens si la limite est atteinte.
Tutoriels frères dans cette série
- Créer une API REST avec Laravel 11
- Eloquent ORM dans Laravel 11 : relations, scopes et casting
- Queues et jobs Laravel 11
- Tester son application avec Pest