Middleware

php artisan make:middleware CheckRole
// app/Http/Middleware/CheckRole.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        if (!$request->user()) {
            return response()->json(['error' => 'Non authentifié'], 401);
        }

        if (!in_array($request->user()->role, $roles)) {
            return response()->json(['error' => 'AccÚs refusé'], 403);
        }

        return $next($request); // Laisser passer la requĂȘte
    }
}

// Middleware aprÚs la réponse (response middleware)
class AddResponseTime
{
    public function handle(Request $request, Closure $next): Response
    {
        $start    = microtime(true);
        $response = $next($request);
        $time     = round((microtime(true) - $start) * 1000, 2);

        return $response->header('X-Response-Time', "{$time}ms");
    }
}
// Enregistrement dans bootstrap/app.php (Laravel 11)
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {

        // Alias pour utilisation dans les routes
        $middleware->alias([
            'role'      => \App\Http\Middleware\CheckRole::class,
            'verified'  => \App\Http\Middleware\EnsureEmailIsVerified::class,
        ]);

        // Middleware global (toutes les requĂȘtes)
        $middleware->append(\App\Http\Middleware\AddResponseTime::class);

        // Groupes
        $middleware->group('api', [
            \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
        ]);
    });
// Utilisation dans les routes
Route::middleware(['auth', 'role:admin'])->group(function() {
    Route::get('/admin', [AdminController::class, 'index']);
});

Route::middleware('role:admin,editor')->get('/dashboard', fn() => view('dashboard'));

Laravel Breeze

composer require laravel/breeze --dev
php artisan breeze:install blade    # avec Blade + Tailwind
# ou
php artisan breeze:install api      # API headless (SPA)

npm install && npm run dev
php artisan migrate

Breeze génÚre automatiquement :

  • Routes : /login, /register, /forgot-password, /reset-password, /verify-email, /logout
  • Controllers dans app/Http/Controllers/Auth/
  • Vues Blade dans resources/views/auth/
  • Migration users dĂ©jĂ  prĂȘte

Sessions & Flash

// Stocker en session
session()->put('user_preferences', ['theme' => 'dark']);
session(['user_id' => 42]);  // équivalent

// Lire
$prefs = session('user_preferences');
$prefs = session('user_preferences.theme', 'light'); // dot notation + défaut

// Vérifier
session()->has('user_preferences');   // clé existe
session()->exists('user_preferences'); // clĂ© existe mĂȘme si null

// Supprimer
session()->forget('user_preferences');
session()->flush(); // tout vider

// Flash data (disponible une seule fois, pour la prochaine requĂȘte)
return redirect()->back()->with('success', 'Article créé !');
// dans la vue : session('success')

// Flash dans le controller
session()->flash('success', 'Opération réussie');

// Reflash (garder pour une requĂȘte supplĂ©mentaire)
session()->reflash();
session()->keep(['success', 'error']); // conserver certaines clés

Guards & Auth

// Helpers Auth
$user    = auth()->user();         // utilisateur courant (null si non auth)
$id      = auth()->id();           // ID de l'utilisateur
$isAuth  = auth()->check();        // est connecté ?
$isGuest = auth()->guest();        // est visiteur ?

// Avec guard spécifique
$adminUser = auth()->guard('admin')->user();

// Connexion manuelle
auth()->login($user);              // connecter
auth()->login($user, true);        // avec "se souvenir de moi"
auth()->loginUsingId(42);
auth()->logout();

// Tentative de connexion
if (auth()->attempt(['email' => $email, 'password' => $password])) {
    request()->session()->regenerate();
    return redirect()->intended('/dashboard');
}
// → retourne false si identifiants incorrects

// Depuis un controller
$user = $request->user();  // plus explicite

Gates

// Définir dans AppServiceProvider::boot()
use Illuminate\Support\Facades\Gate;

Gate::define('update-article', function(User $user, Article $article) {
    return $user->id === $article->author_id || $user->isAdmin();
});

Gate::define('admin-area', fn(User $user) => $user->role === 'admin');

// Vérifier dans un controller
public function update(Request $request, Article $article): JsonResponse {
    // LÚve AuthorizationException (403) si refusé
    $this->authorize('update-article', $article);

    $article->update($request->validated());
    return response()->json(['data' => $article]);
}

// Vérifier sans exception
if (Gate::allows('update-article', $article)) {
    // autorisé
}

if (Gate::denies('admin-area')) {
    abort(403);
}

// Dans Blade
@can('update-article', $article)
    <a href="...">Modifier</a>
@endcan

@cannot('admin-area')
    <p>Zone réservée aux admins</p>
@endcannot

Policies

php artisan make:policy ArticlePolicy --model=Article
// app/Policies/ArticlePolicy.php
namespace App\Policies;

use App\Models\Article;
use App\Models\User;

class ArticlePolicy
{
    // Court-circuite tout pour les admins
    public function before(User $user): ?bool {
        if ($user->isAdmin()) return true;
        return null; // continue vers les méthodes spécifiques
    }

    public function viewAny(?User $user): bool {
        return true; // tout le monde peut lister
    }

    public function view(?User $user, Article $article): bool {
        // PubliĂ© → public. Brouillon → auteur seulement
        return $article->isPublished() || $user?->id === $article->author_id;
    }

    public function create(User $user): bool {
        return in_array($user->role, ['author', 'editor', 'admin']);
    }

    public function update(User $user, Article $article): bool {
        return $user->id === $article->author_id || $user->role === 'editor';
    }

    public function delete(User $user, Article $article): bool {
        return $user->id === $article->author_id;
    }
}

// Utilisation dans le controller
$this->authorize('update', $article);    // auto-détecte ArticlePolicy
$this->authorize('create', Article::class);

// Dans le middleware de route
Route::put('/articles/{article}', [ArticleController::class, 'update'])
    ->can('update', 'article'); // ← auto-dĂ©tecte ArticlePolicy::update

RĂŽles RBAC

// Colonne role sur la table users
// $table->string('role')->default('user');

// Méthodes helpers dans User model
class User extends Authenticatable
{
    public function isAdmin(): bool   { return $this->role === 'admin'; }
    public function isEditor(): bool  { return in_array($this->role, ['editor', 'admin']); }
    public function isAuthor(): bool  { return in_array($this->role, ['author', 'editor', 'admin']); }

    public function hasRole(string ...$roles): bool {
        return in_array($this->role, $roles);
    }
}

// Middleware RoleMiddleware
public function handle(Request $request, Closure $next, string ...$roles): Response {
    if (!$request->user()?->hasRole(...$roles)) {
        abort(403, 'RĂŽle insuffisant.');
    }
    return $next($request);
}
Pour des besoins complexes (permissions granulaires, rĂŽles multiples par utilisateur), installe le package spatie/laravel-permission : composer require spatie/laravel-permission.

Laravel Sanctum

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
// User model : ajouter le trait
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable {
    use HasApiTokens, HasFactory, Notifiable;
}

// Controller d'authentification API
class AuthController extends Controller {

    public function login(Request $request): JsonResponse {
        $credentials = $request->validate([
            'email'    => 'required|email',
            'password' => 'required',
        ]);

        if (!auth()->attempt($credentials)) {
            return response()->json(['message' => 'Identifiants incorrects'], 401);
        }

        $user  = auth()->user();
        $token = $user->createToken('api-token', ['read', 'write']); // abilities

        return response()->json([
            'token'      => $token->plainTextToken,
            'token_type' => 'Bearer',
            'expires_at' => null, // ou now()->addDays(30)
        ]);
    }

    public function logout(Request $request): JsonResponse {
        $request->user()->currentAccessToken()->delete(); // token courant
        // $request->user()->tokens()->delete(); // tous les tokens
        return response()->json(['message' => 'Déconnecté']);
    }

    public function me(Request $request): JsonResponse {
        return response()->json(['data' => $request->user()]);
    }
}

// Routes protégées par Sanctum
Route::middleware('auth:sanctum')->group(function() {
    Route::get('/me', [AuthController::class, 'me']);
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::apiResource('articles', ArticleController::class)->except(['index', 'show']);
});

// Vérifier les abilities
$request->user()->tokenCan('write');
if (!$request->user()->tokenCan('delete')) abort(403);
← Module 04 ▶ Mini-projet 🧠 QCM Module 06 →