Builders Academy
Authentification & Autorisation — JWT, Sessions et OAuth
2 of 2

Chapitre 1 · Leçon 2

RBAC

Middleware d'autorisation basé sur les rôles

Section 1

Implémenter RBAC

Step 1

Authentification vs Autorisation

Authentification : vérifier qui tu es (JWT). Autorisation : vérifier ce que tu peux faire (RBAC). Ces deux couches sont distinctes.

snippet.typescript

typescript
1// types/auth.ts
2export type UserRole = "admin" | "instructor" | "student" | "guest";
3
4export type Permission =
5  | "tutorial:read"    | "tutorial:create" | "tutorial:update" | "tutorial:delete"
6  | "user:read"        | "user:update"     | "user:delete"
7  | "analytics:read";
8
9// Matrice des permissions par rôle
10const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
11  admin: [
12    "tutorial:read", "tutorial:create", "tutorial:update", "tutorial:delete",
13    "user:read", "user:update", "user:delete",
14    "analytics:read",
15  ],
16  instructor: [
17    "tutorial:read", "tutorial:create", "tutorial:update",
18    "user:read",
19  ],
20  student: [
21    "tutorial:read",
22    "user:read",
23  ],
24  guest: [
25    "tutorial:read",
26  ],
27};
28
29// Middleware authenticate — vérifie le JWT
30export function authenticate(req: Request, res: Response, next: NextFunction) {
31  const authHeader = req.headers.authorization;
32  if (!authHeader?.startsWith("Bearer ")) {
33    return res.status(401).json({ error: "Token manquant" });
34  }
35  try {
36    const payload = jwt.verify(
37      authHeader.split(" ")[1],
38      process.env.ACCESS_TOKEN_SECRET!
39    ) as JwtPayload;
40    req.user = { id: payload.sub, role: payload.role as UserRole, email: payload.email };
41    next();
42  } catch {
43    res.status(401).json({ error: "Token invalide ou expiré" });
44  }
45}
46
47// Middleware authorize — vérifie les permissions
48export function authorize(...required: Permission[]) {
49  return (req: Request, res: Response, next: NextFunction) => {
50    const userPerms = ROLE_PERMISSIONS[req.user.role] ?? [];
51    const hasAll = required.every((p) => userPerms.includes(p));
52    if (!hasAll) {
53      return res.status(403).json({ error: "Permissions insuffisantes" });
54    }
55    next();
56  };
57}
58
59// Utilisation dans les routes
60router.post(
61  "/tutorials",
62  authenticate,
63  authorize("tutorial:create"),
64  createTutorialController
65);
66
67router.delete(
68  "/tutorials/:id",
69  authenticate,
70  authorize("tutorial:delete"),
71  deleteTutorialController
72);

snippet.typescript

typescript
1// types/auth.ts
2export type UserRole = "admin" | "instructor" | "student" | "guest";
3
4export type Permission =
5  | "tutorial:read"    | "tutorial:create" | "tutorial:update" | "tutorial:delete"
6  | "user:read"        | "user:update"     | "user:delete"
7  | "analytics:read";
8
9// Matrice des permissions par rôle
10const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
11  admin: [
12    "tutorial:read", "tutorial:create", "tutorial:update", "tutorial:delete",
13    "user:read", "user:update", "user:delete",
14    "analytics:read",
15  ],
16  instructor: [
17    "tutorial:read", "tutorial:create", "tutorial:update",
18    "user:read",
19  ],
20  student: [
21    "tutorial:read",
22    "user:read",
23  ],
24  guest: [
25    "tutorial:read",
26  ],
27};
28
29// Middleware authenticate — vérifie le JWT
30export function authenticate(req: Request, res: Response, next: NextFunction) {
31  const authHeader = req.headers.authorization;
32  if (!authHeader?.startsWith("Bearer ")) {
33    return res.status(401).json({ error: "Token manquant" });
34  }
35  try {
36    const payload = jwt.verify(
37      authHeader.split(" ")[1],
38      process.env.ACCESS_TOKEN_SECRET!
39    ) as JwtPayload;
40    req.user = { id: payload.sub, role: payload.role as UserRole, email: payload.email };
41    next();
42  } catch {
43    res.status(401).json({ error: "Token invalide ou expiré" });
44  }
45}
46
47// Middleware authorize — vérifie les permissions
48export function authorize(...required: Permission[]) {
49  return (req: Request, res: Response, next: NextFunction) => {
50    const userPerms = ROLE_PERMISSIONS[req.user.role] ?? [];
51    const hasAll = required.every((p) => userPerms.includes(p));
52    if (!hasAll) {
53      return res.status(403).json({ error: "Permissions insuffisantes" });
54    }
55    next();
56  };
57}
58
59// Utilisation dans les routes
60router.post(
61  "/tutorials",
62  authenticate,
63  authorize("tutorial:create"),
64  createTutorialController
65);
66
67router.delete(
68  "/tutorials/:id",
69  authenticate,
70  authorize("tutorial:delete"),
71  deleteTutorialController
72);

Topics and skills covered

  • RBAC
  • Authorization
  • Middleware
  • Permissions