0 XP
?
API REST et Backend avec Laravel/Authentification API avec Sanctum
Avancé60 min45 XP

Objectifs de cette leçon

  • Implémenter l'authentification avec Breeze/Jetstream
  • Gérer les rôles et permissions
  • Sécuriser les routes avec des middleware

Authentification API avec Sanctum 🔐

Pourquoi sécuriser une API ?

Une API publique expose des données et des actions. Sans authentification :

  • N'importe qui peut créer, lire, modifier ou supprimer des données
  • Les données sensibles (email, profil) sont accessibles à tous
  • Impossible de savoir qui fait quoi

💡 Sanctum est le package d'authentification officiel de Laravel pour les API. Il utilise des tokens (jetons) plutôt que des sessions.


Installation de Sanctum

Sanctum est inclus dans Laravel 11+ par défaut. Vérifie qu'il est bien installé :

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Le modèle User avec HasApiTokens 🗝️

Le modèle User doit utiliser le trait HasApiTokens :

<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens; // 👈 Ajoute les méthodes createToken(), tokens(), etc.

    protected $fillable = ['name', 'email', 'password'];
    protected $hidden = ['password', 'remember_token'];
}

1. Inscription — POST /api/register 📝

// routes/api.php
Route::post('/register', [AuthController::class, 'register']);
public function register(Request $request)
{
    $validated = $request->validate([
        'name'     => 'required|string|max:255',
        'email'    => 'required|email|unique:users',
        'password' => 'required|string|min:8|confirmed',
    ]);

    $user = User::create([
        'name'     => $validated['name'],
        'email'    => $validated['email'],
        'password' => bcrypt($validated['password']),
    ]);

    // 👇 Crée un token Sanctum
    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json([
        'user'  => $user,
        'token' => $token,
    ], 201);
}

Requête :

POST /api/register
{ "name": "Alice", "email": "alice@ex.com", "password": "secret123", "password_confirmation": "secret123" }

Réponse : 201 Created

{
  "user": { "id": 1, "name": "Alice", "email": "alice@ex.com" },
  "token": "1|abc123def456..."
}

2. Connexion — POST /api/login 🔐

Route::post('/login', [AuthController::class, 'login']);
public function login(Request $request)
{
    $validated = $request->validate([
        'email'    => 'required|email',
        'password' => 'required|string',
    ]);

    $user = User::where('email', $validated['email'])->first();

    if (!$user || !Hash::check($validated['password'], $user->password)) {
        return response()->json([
            'message' => 'Identifiants invalides',
        ], 401);
    }

    // 👇 Supprime les anciens tokens (optionnel)
    $user->tokens()->delete();

    $token = $user->createToken('api-token')->plainTextToken;

    return response()->json([
        'user'  => $user,
        'token' => $token,
    ]);
}

Requête :

POST /api/login
{ "email": "alice@ex.com", "password": "secret123" }

Réponse : 200 OK

{
  "user": { "id": 1, "name": "Alice", "email": "alice@ex.com" },
  "token": "2|def456ghi789..."
}

3. Le token côté client 💾

Le frontend (React, Vue, mobile) doit :

// Après login/register
localStorage.setItem('token', response.data.token);

// Dans chaque requête ultérieure
fetch('/api/user', {
  headers: {
    'Authorization': 'Bearer ' + localStorage.getItem('token'),
    'Accept': 'application/json'
  }
});

⚠️ Sécurité : En production, préfère un cookie HTTP-only plutôt que localStorage pour éviter les attaques XSS.


4. Route protégée 🛡️

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', [UserController::class, 'show']);
    Route::put('/user', [UserController::class, 'update']);
    Route::post('/logout', [AuthController::class, 'logout']);
});
public function show(Request $request)
{
    return response()->json($request->user());
}

Requête :

GET /api/user
Authorization: Bearer 2|def456ghi789...

Réponse : 200 OK

{
  "id": 1,
  "name": "Alice",
  "email": "alice@ex.com",
  "created_at": "2026-01-01T00:00:00.000000Z"
}

Comment Sanctum valide le token ? 🔍

  1. Sanctum extrait le token de l'en-tête Authorization: Bearer ...
  2. Il cherche le hash du token dans la table personal_access_tokens
  3. Il vérifie que le token n'est pas expiré (expires_at)
  4. Il vérifie que le token n'est pas révoqué (deleted_at est NULL)
  5. Si tout va bien → l'utilisateur est authentifié

5. Déconnexion — POST /api/logout 🚪

public function logout(Request $request)
{
    // Supprime TOUS les tokens de l'utilisateur
    $request->user()->tokens()->delete();

    // Ou seulement le token courant :
    // $request->user()->currentAccessToken()->delete();

    return response()->json(['message' => 'Déconnecté']);
}

Requête :

POST /api/logout
Authorization: Bearer 2|def456ghi789...

Réponse : 200 OK

{ "message": "Déconnecté" }

💡 Après logout, le token est supprimé de la BDD. L'utilisateur doit se reconnecter.


Capacités des tokens (Abilities) 🎯

Tu peux limiter ce qu'un token peut faire :

// À la création
$token = $user->createToken('api-token', ['posts:read', 'posts:create']);

// Vérification dans le contrôleur
if (!$request->user()->tokenCan('posts:create')) {
    return response()->json(['message' => 'Action non autorisée'], 403);
}

Bonnes pratiques ✅

  1. Toujours hasher les mots de passe avec bcrypt() ou Hash::make()
  2. Valider les entrées avant de créer un utilisateur
  3. Utiliser un cookie HTTP-only plutôt que localStorage en production
  4. Supprimer les anciens tokens au login pour limiter les sessions actives
  5. Ajouter une expiration aux tokens : createToken('api-token', ['*'], now()->addDays(7))
  6. Utiliser des capacités (abilities) pour le contrôle d'accès fin
  7. Ne jamais retourner le mot de passe dans les réponses JSON

Pièges fréquents 🚨

PiègeSolution
❌ Oublier Hash::check()Toujours comparer les mots de passe avec Hash::check(), pas avec ==
❌ Stocker le token en clair dans l'URLToujours dans l'en-tête Authorization, jamais dans l'URL
❌ Ne pas gérer l'expiration des tokensAjoute expires_at dans la migration Sanctum
❌ Retourner le token dans les réponses GETLe token est émis une fois (login/register), pas dans chaque réponse
❌ Ignorer HTTPSEn production, force HTTPS avec ForceFirstParty ou un middleware

Exercices pour toi 🎯

  1. Pratique : Crée un AuthController complet avec register, login, logout, et user.
  2. Routes : Enregistre les routes dans routes/api.php — login et register publiques, logout et user protégées par auth:sanctum.
  3. Test : Utilise Postman ou curl pour tester le cycle complet : register → login → GET /api/user → logout.
  4. Capacités : Ajoute des abilities à ton token : posts:read, posts:create. Vérifie-les dans le contrôleur.
  5. Défi : Ouvre le simulateur ci-dessous. Parcours le cycle complet d'authentification : inscription → connexion → token → route protégée → déconnexion.

⚡ Simulateur — Authentification API avec Sanctum

Étape 1/7
📝 Pseudo-code
1# Authentification API avec Sanctum
2POST /api/register → User::create() # ⬅ inscription
3POST /api/login → return $user->createToken() # ⬅ connexion
4Client stocke le token dans Authorization: Bearer... # ⬅ token stocké
5GET /api/user → Route::middleware('auth:sanctum')... # ⬅ route protégée
6Sanctum valide le token → middleware passe # ⬅ validation
7return response()->json($user) # ⬅ données utilisateur
8POST /api/logout → $request->user()->tokens()->delete() # ⬅ déconnexion
🔐 Auth avec Sanctum
📝 Register🔐 Login💾 Stockage🛡️ Protégée Validé📦 Données🚪 Logout
🔑 Token :🔑 Token créé
Créer un compte utilisateur
📤 Requête
POST/api/register
{"name":"Alice","email":"alice@ex.com","password":"secret123"}
🐘 Code
User::create($request->validated());
$token = $user->createToken('api-token')->plainTextToken;
return response()->json(['token' => $token], 201);
📦 Réponse201
{"token":"1|abc123..."}
💡 L'utilisateur fournit name, email, password. Laravel crée le compte ET génère un token Sanctum immédiatement.
💡 Cycle d'authentification Sanctum
📝 Register🔐 Login🛡️ Token🔑 Bearer✅ Validé📦 Données🚪 Logout

register() crée l'utilisateur et génère un token Sanctum. Le frontend doit stocker ce token.