Objectif

Sécuriser le blog avec un système RBAC complet : middleware custom, policies Eloquent, tokens Sanctum, et une API d'authentification complète (register/login/logout/me).

ConceptMis en pratique
MiddlewareCheckRole enregistré dans bootstrap/app.php
GatesAccès zone admin
PolicyArticlePolicy : before() admin bypass + méthodes CRUD
SanctumToken API avec abilities read / write
Auth APIregister / login / logout / me dans AuthController

Matrice des rôles

ActionGuestUserAuthorEditorAdmin
Lire articles publiés
Créer un article
Modifier son article
Modifier tout article
Supprimer tout article
Accès /admin

Consignes

  1. Ajouter la colonne role (default: 'user') à la table users via migration
  2. Créer CheckRole middleware avec string ...$roles variadic
  3. Enregistrer l'alias 'role' dans bootstrap/app.php
  4. Créer ArticlePolicy : méthodes view, create, update, delete + before() pour admin
  5. Créer AuthController avec register/login/logout/me pour l'API Sanctum
  6. Protéger les routes API avec auth:sanctum et les abilities

Solution commentée

// 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 $request->expectsJson()
                ? response()->json(['message' => 'Non authentifié.'], 401)
                : redirect()->route('login');
        }

        if (!$request->user()->hasRole(...$roles)) {
            return $request->expectsJson()
                ? response()->json(['message' => 'Accès refusé.'], 403)
                : abort(403);
        }

        return $next($request);
    }
}

// bootstrap/app.php — Laravel 11
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \App\Http\Middleware\CheckRole::class,
    ]);
})
// app/Policies/ArticlePolicy.php
namespace App\Policies;

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

class ArticlePolicy
{
    // Admins passent partout
    public function before(User $user): ?bool {
        return $user->isAdmin() ? true : null;
    }

    public function viewAny(?User $user): bool { return true; }

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

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

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

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

// app/Models/User.php — méthodes helper
public function isAdmin(): bool  { return $this->role === 'admin'; }
public function isEditor(): bool { return in_array($this->role, ['editor', 'admin']); }

public function hasRole(string ...$roles): bool {
    return in_array($this->role, $roles);
}
// app/Http/Controllers/Api/AuthController.php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function register(Request $request): JsonResponse {
        $data = $request->validate([
            'name'                  => 'required|string|max:255',
            'email'                 => 'required|email|unique:users',
            'password'              => 'required|min:8|confirmed',
        ]);

        $user  = User::create([...$data, 'password' => bcrypt($data['password'])]);
        $token = $user->createToken('api-token', ['read']);

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

    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();
        $abilities = $user->hasRole('author', 'editor', 'admin') ? ['read', 'write'] : ['read'];
        $token = $user->createToken('api-token', $abilities);

        return response()->json([
            'token'      => $token->plainTextToken,
            'token_type' => 'Bearer',
            'user'       => ['id' => $user->id, 'name' => $user->name, 'role' => $user->role],
        ]);
    }

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

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

// routes/api.php
Route::prefix('v1')->group(function () {
    Route::post('/auth/register', [AuthController::class, 'register']);
    Route::post('/auth/login',    [AuthController::class, 'login']);

    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/auth/me',      [AuthController::class, 'me']);
        Route::post('/auth/logout', [AuthController::class, 'logout']);

        Route::middleware('ability:read')->group(function () {
            Route::apiResource('articles', ArticleController::class)->only(['index', 'show']);
        });

        Route::middleware('ability:write')->group(function () {
            Route::apiResource('articles', ArticleController::class)->except(['index', 'show']);
        });
    });
});
← Cours Module 05 🧠 QCM Module 06 →