security
JWT avec jose, et pourquoi pas jsonwebtoken
JWT avec jose, et pourquoi pas jsonwebtoken
jose est une implémentation JS/TS de la famille de specs JOSE — JWS, JWE, JWK, JWT — agnostique au framework, fonctionnant dans Node, les navigateurs, Deno et les edge runtimes. Je l'utilise dans le back-end au lieu du plus souvent tutorialisé jsonwebtoken.
jsonwebtoken est CommonJS d'abord et a eu des CVE autour de la confusion d'algorithme — signer avec alg: none, ou vérifier un jeton RS256 avec la clé publique traitée comme un secret HMAC si tu ne verrouilles pas l'algorithme. jose force l'explicité sur l'algorithme à la signature et à la vérification, sans confiance ambiante dans ce que l'en-tête du jeton prétend. C'est de l'ESM natif avec des types corrects, et ça marche partout où la stack pourrait avoir besoin d'un edge runtime, ce que jsonwebtoken ne peut pas.
import { SignJWT, jwtVerify } from "jose";
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const token = await new SignJWT({ sub: userId, role: "user" })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("15m")
.sign(secret);
const { payload } = await jwtVerify(token, secret, {
algorithms: ["HS256"], // épinglé — ne laisse jamais le jeton dicter le sien
});
Les clés et secrets sont des Uint8Array, pas des chaînes brutes — le plus grand ajustement en venant de jsonwebtoken, et passer une chaîne brute là où un Uint8Array est attendu échoue silencieusement de façons déroutantes.
Le pattern qui a vraiment compté
Des jetons sans état plus un coupe-circuit avec état. Les JWT sont sans état par conception — pas de recherche en base pour valider une signature, ce qui est tout le gain de performance — mais ça veut dire pas de révocation anticipée non plus. Ça m'a mordu directement : un utilisateur verrouillé pouvait continuer à faire des requêtes authentifiées jusqu'à l'expiration naturelle, parce que rien ne suivait si un jeton devait encore être de confiance. Le correctif n'était pas une fonctionnalité de jose, c'était architectural — un token_version sur l'utilisateur, intégré comme revendication, comparé à la valeur courante en base à chaque requête. Les sign/verify de jose ne sont que des primitives ; le cycle de vie de la session doit être construit autour d'elles.
Les jetons d'accès restent de courte durée ; les jetons de rafraîchissement sont de plus longue durée et httpOnly, hors de portée du XSS. Un current_refresh_jti sur l'utilisateur suit et fait tourner l'unique jeton de rafraîchissement valide par session et détecte la réutilisation — un jeton de rafraîchissement déjà pivoté rejoué est un signe classique de vol.