đŸ‘ïž 153 vues

Guide complet — Gestion des Tokens sous Express.js

1. Introduction aux Tokens

Dans Express.js, un token est une preuve d’identitĂ© permettant d’autoriser un utilisateur ou un service Ă  accĂ©der Ă  des ressources protĂ©gĂ©es.

Le serveur Ă©met un token aprĂšs authentification, puis le client l’envoie Ă  chaque requĂȘte (souvent dans le header Authorization).

Les tokens remplacent les sessions classiques (cookies de session) dans les architectures modernes d’API REST ou microservices.


2. Les différentes approches modernes

A. JWT (JSON Web Token)

Un JWT est un jeton auto-contenu, signé par le serveur, qui contient des informations encodées (sub, email, role, etc.).

BibliothĂšques modernes :

Structure d’un JWT :

header.payload.signature

Avantages :

  • Pas de stockage cĂŽtĂ© serveur.
  • VĂ©rification rapide (signature HMAC ou RSA).
  • Standard international.

Inconvénients :

  • Difficile Ă  rĂ©voquer.
  • Si compromis, le token reste valide jusqu’à expiration.
  • VulnĂ©rable si stockĂ© dans localStorage.

B. JWT + Refresh Token

Cette approche utilise deux jetons :

  • Access Token (courte durĂ©e, ex : 15 minutes)
  • Refresh Token (longue durĂ©e, ex : 7 jours)

L’Access Token est stockĂ© en mĂ©moire, le Refresh Token en cookie HttpOnly + Secure.

Cela permet un renouvellement transparent de session sans redemander le mot de passe.


Un JWT peut ĂȘtre stockĂ© directement dans un cookie HttpOnly et Secure, qui remplace la session traditionnelle.

  • L’auth se fait automatiquement avec le cookie.
  • Le serveur vĂ©rifie le JWT sans base de donnĂ©es.
  • IdĂ©al pour des applications full web (SSR, Next.js, etc.)

D. OIDC / OAuth2

OpenID Connect (OIDC) et OAuth2 sont utilisés pour :

  • L’authentification via tiers (Google, Microsoft, GitHub
)
  • Le Single Sign-On (SSO)
  • L’accĂšs interservices sĂ©curisĂ©

Libs utiles :


E. Token Opaque + Redis

Le token opaque est un identifiant aléatoire sans signification :

3baf4d90-e95c-46f7-bb1f-3d27a36c6e52

Il ne contient aucune donnée utilisateur.
Le serveur stocke l’association dans Redis :

SET token:3baf4d90 { "userId": 42, "role": "admin" } EX 900

À chaque requĂȘte, le serveur consulte Redis pour vĂ©rifier la validitĂ© du token.

Avantages :

  • RĂ©vocation immĂ©diate (supprimer la clĂ© Redis)
  • Aucune donnĂ©e exposĂ©e au client
  • ContrĂŽle total cĂŽtĂ© serveur

Inconvénients :

  • Redis devient un point central (stateful)
  • NĂ©cessite un accĂšs rĂ©seau rapide et fiable

3. Comparaison entre JWT et Token Opaque

CritĂšre JWT Token Opaque + Redis
Structure Auto-contenu Identifiant aléatoire
Stockage CÎté client CÎté serveur
Vérification Locale (signature) Consultation Redis
Révocation Complexe Instantanée
Sécurité Bonne TrÚs élevée
Scalabilité Excellente Bonne mais centralisée
Débogage Facile (lisible) Requiert Redis
Usage typique API publiques, microservices API internes, sécurité stricte

4. Concept de Stateless vs Stateful

A. Qu’est-ce qu’une API Stateless

Une API stateless ne garde aucun Ă©tat entre les requĂȘtes. Chaque requĂȘte contient toutes les informations nĂ©cessaires Ă  son traitement.

Exemple : Un JWT est stateless : il contient toutes les infos (id, rĂŽle, expiration).
Aucun appel externe n’est nĂ©cessaire.

B. Qu’est-ce qu’une API Stateful

Une API stateful conserve l’état cĂŽtĂ© serveur (ex : Redis, sessions).
Les requĂȘtes dĂ©pendent d’informations mĂ©morisĂ©es sur le serveur.

Exemple : Un token opaque nécessite de consulter Redis pour savoir à quel utilisateur il correspond.

C. Impact dans une architecture microservices

  • JWT (stateless) : chaque microservice peut vĂ©rifier les tokens seul.
  • Redis (stateful) : chaque service dĂ©pend du store commun.

Conséquence :

  • Le modĂšle stateful ajoute une dĂ©pendance centrale (Redis), rĂ©duisant l’autonomie et la rĂ©silience.
  • En revanche, il permet la rĂ©vocation immĂ©diate et un contrĂŽle total.

5. Redis dans la gestion des Tokens

A. Qu’est-ce que Redis

Redis est une base clé/valeur en mémoire ultra-rapide, utilisée pour :

  • le cache,
  • les sessions,
  • les files d’attente,
  • les compteurs,
  • la communication temps rĂ©el.

Redis stocke les données directement dans la RAM, ce qui le rend des dizaines de fois plus rapide que les bases de données classiques.


B. Cas d’usage concrets avec Redis

  1. Cache ultra-rapide
  2. Gestion des sessions et tokens
  3. File d’attente (queue)
  4. Rate limiting
  5. Pub/Sub
  6. Stockage temporaire
  7. Compteurs et statistiques

C. Pourquoi Redis est si rapide

  • DonnĂ©es en RAM
  • OpĂ©rations atomiques
  • Protocole rĂ©seau minimaliste
  • Mono-thread
  • OptimisĂ© pour la latence microseconde

D. Intégration Redis avec Express.js

npm install redis

Exemple simple :

const { createClient } = require("redis");
const client = createClient();

client.connect();

app.get("/", async (req, res) => {
  const count = await client.incr("visits");
  res.send(`Visites : ${count}`);
});

6. Bonnes pratiques de sécurité 2025

Risque Mesure recommandée
Token volé Durée courte + refresh token
Vol cookie HttpOnly + Secure + SameSite=Strict
Secret exposé Stocker la clé dans .env
Token forgé Signature HMAC ou RSA
Révocation Gérer une denylist ou Redis
Environnement HTTP Toujours utiliser HTTPS
Vérification Toujours vérifier signature et expiration

7. Exemples de code Express.js

JWT avec JOSE

import { SignJWT, jwtVerify } from "jose";

const secret = new TextEncoder().encode(process.env.JWT_SECRET);

export async function signToken(payload, expiresIn = "15m") {
  return await new SignJWT(payload)
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime(expiresIn)
    .sign(secret);
}

export async function verifyToken(token) {
  const { payload } = await jwtVerify(token, secret);
  return payload;
}

router.post("/login", async (req, res) => {
  const accessToken = await signToken({ sub: user.id }, "15m");
  const refreshToken = await signToken({ sub: user.id, type: "refresh" }, "7d");

  res.cookie("refresh_token", refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: "Strict",
    maxAge: 7 * 24 * 60 * 60 * 1000,
  });

  res.json({ accessToken });
});

Token Opaque + Redis

const { v4: uuidv4 } = require("uuid");
const redis = require("../utils/redis");

exports.login = (req, res) => {
  User.findOne({ email: req.body.email })
    .then((user) => {
      if (!user) return res.status(401).json({ error: "Utilisateur introuvable" });
      const token = uuidv4();
      redis.set(token, JSON.stringify({ id: user._id }), { EX: 900 });
      res.json({ token });
    })
    .catch((err) => res.status(500).json({ error: "Erreur serveur" }));
};

Fin du document.

Il n'y a actuellement aucun commentaire, alors soyez le premier !