Guide des mécanismes d’authentification dans les applications Web modernes
Ce document présente les mécanismes d’authentification utilisés dans les applications web modernes, leurs avantages, inconvénients, domaines d’usage, ainsi que les bonnes pratiques de sécurité issues des recommandations de l’OWASP et des standards actuels.
1. Qu’est-ce que l’OWASP ?
Présentation
OWASP (Open Worldwide Application Security Project) est une organisation internationale à but non lucratif dédiée à l’amélioration de la sécurité des applications web et logicielles.
Elle fournit :
- des guides de bonnes pratiques,
- des standards de sécurité,
- des outils open source,
- des référentiels reconnus mondialement.
L’OWASP est indépendante des éditeurs et des technologies, ce qui rend ses recommandations largement adoptées dans l’industrie.
OWASP Top 10
Le document le plus connu est le OWASP Top 10, qui recense les dix catégories de vulnérabilités les plus critiques. Plusieurs concernent directement l’authentification et la gestion des identités, notamment :
- Authentification défaillante,
- Mauvaise gestion des sessions,
- Contrôles d’accès cassés,
- Exposition de données sensibles.
Principe fondamental OWASP
Le client (navigateur, SPA, application mobile) ne doit jamais être considéré comme fiable.
Toute logique de sécurité doit être implémentée et validée côté serveur.
2. Concepts fondamentaux
Authentification vs autorisation
- L’authentification consiste à vérifier l’identité d’un utilisateur ou d’un système.
- L’autorisation consiste à vérifier ce que cet utilisateur ou système est autorisé à faire.
Une application sécurisée doit traiter ces deux notions séparément.
3. Authentification par session et cookies (Web traditionnel)
Principe
Après la saisie d’un identifiant et d’un mot de passe, le serveur crée une session et transmet un identifiant de session au navigateur via un cookie HTTP.
Le navigateur renvoie automatiquement ce cookie à chaque requête suivante.
Avantages
- Mécanisme simple et éprouvé,
- Excellente intégration avec les navigateurs,
- Contrôle total côté serveur,
- Facile à mettre en œuvre dans les applications web traditionnelles.
Inconvénients
- Peu adapté aux API REST,
- Scalabilité plus complexe en environnement distribué,
- Fortement couplé au navigateur.
Sécurité (OWASP)
- Protection contre les attaques CSRF obligatoire,
- Cookies configurés avec
HttpOnly, Secure et SameSite,
- Invalidation systématique des sessions à la déconnexion.
4. Authentification HTTP Basic
Principe
Les identifiants sont envoyés à chaque requête HTTP via l’en-tête Authorization, encodés en Base64.
Avantages
- Très simple à implémenter,
- Supporté nativement par HTTP,
- Utile pour des tests ou des usages temporaires.
Inconvénients
- Aucun mécanisme de session,
- Le mot de passe est transmis à chaque requête,
- Sécurité entièrement dépendante de HTTPS.
Recommandation
Cette méthode est déconseillée en production pour des utilisateurs finaux.
5. JWT (JSON Web Token)
Principe
Le serveur délivre un token signé contenant des informations appelées claims.
Le token est auto-contenu et peut être vérifié sans accès à une base de données.
Avantages
- Stateless,
- Très scalable,
- Compatible avec SPA, applications mobiles et microservices,
- Standard largement adopté.
Inconvénients
- Révocation complexe,
- Risques importants en cas de mauvaise implémentation,
- Sécurité fortement dépendante du mode de stockage côté client.
Recommandations OWASP
- Ne jamais stocker un JWT dans
localStorage,
- Préférer les cookies
HttpOnly côté navigateur,
- Utiliser des tokens de courte durée,
- Mettre en place des refresh tokens,
- Ne jamais stocker de données sensibles dans le token.
6. Authentification par token opaque
Principe
Le serveur génère un token aléatoire sans signification intrinsèque.
Ce token est stocké côté serveur et vérifié à chaque requête.
Avantages
- Révocation simple,
- Aucune donnée exposée côté client,
- Plus sûr que les JWT dans certains contextes.
Inconvénients
- Vérification serveur nécessaire à chaque requête,
- Moins performant que les JWT à très grande échelle.
7. Cas d’usage : authentification dans une SPA (frontend)
Problématique
Une application frontend (React, Vue, Angular, etc.) ne peut pas protéger un secret.
Tout code exécuté côté client est potentiellement accessible ou modifiable.
Bonnes pratiques
- Authentification réalisée via une API backend,
- Stockage du token :
- Cookie
HttpOnly (recommandé),
- Mémoire volatile (state JavaScript),
- Protection contre les attaques XSS, CSRF et le rejeu de tokens.
Reco
CommonJS vs ECMAScript (ESM)
📘 Comprendre les fichiers .mjs dans Node.js
🧩 1. .mjs, c’est quoi ?
Un fichier .mjs est un module ECMAScript — c’est-à-dire un fichier JavaScript utilisant la syntaxe moderne des imports et exports, comme celle des navigateurs.
// fichier.mjs
import express from "express";
import { maFonction } from "./utils.mjs";
export const app = express();
👉 L’extension .mjs indique à Node.js que le fichier doit être interprété en mode ESM (ECMAScript Module)
et non en CommonJS (le système historique de Node).
⚙️ 2. Contexte historique
Avant 2020, Node.js utilisait uniquement le système CommonJS :
const express = require("express");
module.exports = app;
Mais depuis Node.js 14+, il supporte aussi les modules ECMAScript (mêmes modules que les navigateurs).
Pour éviter toute ambiguïté, Node a introduit une distinction stricte entre :
.js (fichiers interprétés comme CommonJS par défaut)
.mjs (fichiers interprétés comme modules ECMAScript)
⚙️ 3. Les deux systèmes de modules
| Aspect |
CommonJS (.js) |
ESM (.mjs ou type: "module") |
| Importation |
const X = require("x") |
import X from "x" |
| Exportation |
module.exports = X |
export default X |
| Chargement |
Synchrone |
Asynchrone |
| Scope |
Global par fichier |
Strict (par module) |
| Compatibilité historique |
✅ Node.js classique |
✅ Node 14+ |
| Syntaxe moderne |
❌ Non |
✅ Oui |
🧠 4. Exemple comparatif
🟠 CommonJS (.js)
// utils.js
const sum = (a, b) => a + b;
module.exports = { sum };
// app.js
const { sum } = require("./utils.js");
console.log(sum(2, 3));
🔵 ECMAScript (.mjs)
// utils.mjs
export function sum(a, b) {
return a + b;
}
// app.mjs
import { sum } from "./utils.mjs";
console.log(sum(2, 3));
🧩 5. Et si je veux rester en .js ?
Tu peux utiliser la syntaxe import/export sans passer en .mjs
en ajoutant ce paramètre dans ton package.json :
{
"type": "module"
}
➡️ Dans ce cas, tous les fichiers .js du projet sont interprétés comme des modules ESM.
Tu n’as donc plus besoin de .mjs.
⚠️ Mais attention :
- Tu ne pourras plus utiliser
require() ni module.exports directement.
- Tu devras tout passer en
import/export.
🧱 6. Quand utiliser .mjs plutôt que .js ?
| Situation |
Recommandation |
| Petit projet Node classique (Express, Mongo, etc.) |
✅ Reste en .js (CommonJS) |
| Projet moderne, TypeScript, ou compatible navigateur |
✅ Utilise .mjs ou "type": "module" |
| Tu veux mélanger les deux systèmes |
⚠️ Possible mais à éviter |
| Librairie NPM moderne à publier |
✅ .mjs recommandé pour compatibilité ESM |
🧩 7. Mélanger CommonJS et ESM (cas avancé)
Il est possible de mélanger les deux, mais c’est souvent source de confusion.
Exemple :
Mais cela complique la maintenance : le mieux est de choisir un seul style.
✅ 8. En résumé
| Élément |
.js (CommonJS) |
.mjs (ESM) |
| Import |
require() |
import |
| Export |
module.exports |
export |
| Système |
Ancien (Node) |
Standard ECMAScript |
| Compatibilité |
Universelle |
Moderne |
| Recommandé en 2025 |
⚙️ pour rétrocompatibilité |
🟢 pour les projets modernes |
💡 En clair :
.js = modules CommonJS (require, module.exports)
.mjs = modules ECMAScript (import, export)
- Tu peux aussi garder
.js et déclarer "type": "module" dans ton package.json si tu veux la syntaxe moderne partout.
VIM
https://www.vim-hero.com/
https://vim-adventures.com/
404
Not Found
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.
C. JWT stocké en Cookie sécurisé
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
- Cache ultra-rapide
- Gestion des sessions et tokens
- File d’attente (queue)
- Rate limiting
- Pub/Sub
- Stockage temporaire
- 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;
}
JWT + Refresh Token en Cookie HttpOnly
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.