backend / php-laravel
Authentification : Breeze et Sanctum
Explication
Ce que vous allez apprendre
- Distinguer clairement quand utiliser Breeze (sessions web) et Sanctum (tokens API/SPA)
- Générer et vérifier un token d'API avec des permissions (abilities) limitées
- Protéger des routes API avec le middleware
auth:sanctum - Créer des Policies pour centraliser l'autorisation fine par modèle
- Comprendre pourquoi le mot de passe ne doit jamais apparaître dans une réponse JSON
Dans quel contexte ?
Une même application Laravel doit servir à la fois une interface web classique (avec connexion par formulaire et session) et une API consommée par une application mobile qui ne peut pas gérer de cookies de session. Ces deux besoins d'authentification, bien que liés, demandent des outils différents : Breeze pour le premier, Sanctum pour le second.
D'abord, deux besoins d'authentification fondamentalement différents
Breeze fournit un scaffolding complet d'authentification classique : formulaire de connexion, inscription, réinitialisation de mot de passe, le tout reposant sur des sessions et des cookies — exactement ce qu'attend un navigateur web. Sanctum, à l'inverse, cible deux cas : des tokens d'API pour des clients externes (mobile, scripts tiers), et une authentification par cookie stateful pour des SPA du même domaine que le backend.
| Solution | Mécanisme | Cas d'usage |
|---|---|---|
| Breeze | Sessions + cookies | Application web classique avec formulaires |
| Sanctum (tokens) | Bearer token dans l'en-tête | API consommée par mobile ou service tiers |
| Sanctum (SPA) | Cookie stateful même domaine | Frontend JS séparé mais sur le même domaine |
Prérequis
Il faut connaître les middlewares (leçon précédente), puisque l'authentification Sanctum s'applique via ->middleware('auth:sanctum') sur les routes protégées.
Ensuite, un token n'a pas besoin d'avoir tous les droits
$user->createToken('mobile-app', ['produits:lire']) génère un token avec des "abilities" limitées — ce token ne pourra, par exemple, que lire des produits, jamais en créer ou en supprimer, même s'il est intercepté par un attaquant. C'est un principe de sécurité fondamental : donner à chaque token le minimum de droits nécessaire.
Piège fréquent
Créer systématiquement des tokens avec toutes les permissions (createToken('app') sans abilities précisées) revient à annuler l'intérêt du système d'abilities. Toujours réfléchir, dès la création du token, à ce dont ce client a réellement besoin.
Il reste à protéger l'exposition accidentelle du mot de passe
Le tableau $hidden = ['password', 'remember_token'] sur le modèle User garantit que ces champs ne sont JAMAIS inclus quand le modèle est converti en JSON, même par erreur d'un développeur qui oublierait de les exclure manuellement. C'est une protection automatique et systématique, à ne jamais retirer.
Astuce
protected function casts(): array { return ['password' => 'hashed']; } hache automatiquement le mot de passe (avec bcrypt) dès qu'on l'assigne au modèle, sans jamais avoir à appeler Hash::make() manuellement à chaque endroit du code où un mot de passe est défini.
Maintenant, une autorisation plus fine que "connecté ou pas"
Savoir qu'un utilisateur est connecté ne suffit pas toujours : encore faut-il savoir s'il a le droit de modifier CE produit précis. Une Policy (ProduitPolicy) centralise cette logique par modèle, testable indépendamment, et appelable aussi bien depuis un contrôleur ($this->authorize('update', $produit)) que depuis une vue Blade (@can('update', $produit)).
Maintenant que l'authentification et l'autorisation sont en place, la prochaine leçon montre comment structurer proprement les réponses JSON d'une API avec les Resources Laravel, et comment gérer leur versionnement dans le temps.
Commandes & code
Authentification : Breeze et Sanctum
# Laravel Breeze : scaffolding d'authentification classique (sessions, cookies)
composer require laravel/breeze --dev
php artisan breeze:install blade # ou vue / react / api selon le frontend
php artisan migrate
npm install && npm run build
# Laravel Sanctum : tokens API + authentification SPA (cookies stateful pour SPA du même domaine)
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate<?php
// app/Models/User.php : le trait HasApiTokens ajoute les capacités Sanctum
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable {
use HasApiTokens, HasFactory, Notifiable;
protected $fillable = ['nom', 'email', 'password'];
protected $hidden = ['password', 'remember_token']; // jamais sérialisés en JSON
protected function casts(): array {
return ['password' => 'hashed']; // hash automatique à l'assignation (bcrypt)
}
}
// Contrôleur de connexion API : génère un token personnel
namespace App\Http\Controllers\Auth;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class AuthTokenController extends Controller {
public function login(Request $request) {
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
$user = User::where('email', $credentials['email'])->first();
if (!$user || !\Hash::check($credentials['password'], $user->password)) {
throw ValidationException::withMessages([
'email' => ['Identifiants invalides.'],
]);
}
// Token avec abilities (scopes) limitées : ce token ne peut que lire
$token = $user->createToken('mobile-app', ['produits:lire'])->plainTextToken;
return response()->json(['token' => $token]);
}
public function logout(Request $request) {
$request->user()->currentAccessToken()->delete();
return response()->noContent();
}
}<?php
// routes/api.php : protéger les routes avec le middleware Sanctum
Route::middleware('auth:sanctum')->group(function () {
Route::get('/moi', fn (Request $request) => $request->user());
// Vérifier une ability spécifique dans un contrôleur ou middleware
Route::post('/produits', [ProduitController::class, 'store'])
->middleware('abilities:produits:ecrire');
});<?php
// Policies : autorisation fine par modèle (app/Policies/ProduitPolicy.php)
namespace App\Policies;
use App\Models\{User, Produit};
class ProduitPolicy {
public function update(User $user, Produit $produit): bool {
return $user->id === $produit->createur_id || $user->hasRole('admin');
}
public function delete(User $user, Produit $produit): bool {
return $user->hasRole('admin');
}
}
// Utilisation : $this->authorize('update', $produit); dans un contrôleur
// ou @can('update', $produit) dans une vue BladeRésumé
- Breeze = authentification web classique par sessions ; Sanctum = tokens API et SPA du même domaine.
HasApiTokens+createToken('nom', ['ability'])génère des tokens à portée limitée (abilities).- Les Policies centralisent les règles d'autorisation par modèle, testables indépendamment des contrôleurs.
'password' => 'hashed'dans les casts hash automatiquement le mot de passe à l'assignation.
Exercices pratiques
Mission : limiter les degats d'un token API vole
Objectif : Corriger une generation de token trop permissive et ajouter une Policy pour une autorisation fine par produit.
Contexte
Le endpoint de connexion mobile genere ses tokens avec $user->createToken('mobile-app')->plainTextToken, sans preciser la moindre ability. Un audit de securite signale que si ce token venait a fuiter (log mal configure, interception reseau), il donnerait acces a TOUTES les actions de l'API, y compris la suppression de produits, alors que l'application mobile ne fait jamais que consulter des produits. Par ailleurs, n'importe quel utilisateur connecte peut actuellement modifier n'importe quel produit via l'API, meme s'il ne l'a pas cree lui-meme.