Hachez les mots de passe avec une fonction coûteuse, choisissez entre sessions et JWT en connaissance de cause et protégez vos routes par middleware.
Ouvrir cette leçon dans KodokonStocker un mot de passe, c'est préparer une future fuite : concevez le hachage pour résister au vol de la base. Un hachage rapide type SHA-256 se brute-force à plusieurs milliards d'essais par seconde sur GPU. Il faut une fonction volontairement coûteuse - scrypt, fourni par node:crypto, ou bcrypt/argon2 - et un sel aléatoire par utilisateur, qui rend les tables précalculées inutilisables. Le sel n'est pas un secret : il se stocke en clair à côté du hachage.
import {
randomBytes,
scryptSync,
timingSafeEqual,
} from 'node:crypto';
export function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const hash = scryptSync(password, salt, 64)
.toString('hex');
return `${salt}:${hash}`;
}
export function verifyPassword(password, stored) {
const [salt, hash] = stored.split(':');
const candidate = scryptSync(password, salt, 64);
const expected = Buffer.from(hash, 'hex');
return timingSafeEqual(candidate, expected);
}Deux modèles pour maintenir l'utilisateur connecté. La session : un identifiant opaque dans un cookie httpOnly, l'état côté serveur. Révocation immédiate, cookie invisible pour le JavaScript, mais un stockage partagé (Redis) devient nécessaire dès la deuxième instance. Le JWT : l'état signé embarqué dans le jeton, vérifiable sans stockage - idéal entre services - mais irrévocable avant expiration. Le compromis honnête : sessions pour une application web classique, JWT courts pour les API consommées par des tiers ou les architectures distribuées.
import jwt from 'jsonwebtoken';
const SECRET = process.env.JWT_SECRET;
if (!SECRET) {
throw new Error('JWT_SECRET must be set');
}
export function signToken(user) {
return jwt.sign(
{ sub: user.id, email: user.email },
SECRET,
{ expiresIn: '15m' },
);
}Le middleware de protection factorise le contrôle d'accès : il extrait le jeton de l'en-tête Authorization: Bearer, le vérifie, attache l'identité décodée à req.user, ou coupe court avec un 401 acheminé vers le middleware d'erreur de la leçon précédente. Appliquez-le au niveau d'un routeur (router.use(requireAuth)) pour protéger un groupe de routes entier plutôt que route par route.
import jwt from 'jsonwebtoken';
import { HttpError } from './http-error.js';
const SECRET = process.env.JWT_SECRET;
export function requireAuth(req, res, next) {
const header = req.headers.authorization ?? '';
const [scheme, token] = header.split(' ');
if (scheme !== 'Bearer' || !token) {
return next(new HttpError(401, 'Missing token'));
}
try {
req.user = jwt.verify(token, SECRET);
next();
} catch {
next(new HttpError(401, 'Invalid or expired token'));
}
}timingSafeEqual dans la vérification du mot de passe ?