jsonwebtoken signe et vérifie des JSON Web Tokens avec trois appels : jwt.sign(), jwt.verify() et jwt.decode(). Il gère la couche JWS au moyen des paquets jws et jwa et confie la cryptographie à node:crypto, si bien que les clés peuvent être des chaînes PEM, des buffers ou des instances KeyObject. jwt.decode() ne fait qu’analyser le jeton : cette fonction ne vérifie jamais la signature, et le code qui en lit les claims se fie à tout ce que l’expéditeur y a écrit.
La version 9 a fermé les voies classiques de confusion d’algorithme, et les essais l’ont confirmé pour la 9.0.3 : sans option algorithms, la liste autorisée découle du type de clé, un jeton HS256 dont la clé est le texte d’une clé publique est refusé, et alg: none ne passe que si vous listez none et ne fournissez aucune clé. Passez tout de même algorithms : l’option documente le contrat et continue de fonctionner si le type de clé change un jour.
La liste des algorithmes s’arrête à l’ensemble de la RFC 7518 : il n’y a ni EdDSA, ni Ed25519, ni ES256K. Un jeton d’un émetteur qui signe en Ed25519 (courant avec jose et dans les écosystèmes Go et Rust) ne peut pas être vérifié ici ; utilisez plutôt jose. ES256 exige aussi une clé P-256, et signer avec une clé P-384 sous ce nom lève une exception au lieu de produire un jeton que d’autres bibliothèques rejetteraient.
Les signatures ECDSA contenues dans le jeton sont sous la forme r‖s brute de 64 octets qu’impose JWS. Quand un jeton a été assemblé à la main à partir d’openssl dgst, de SHA256withECDSA de Java ou d’un KMS cloud, sa signature est en DER et la vérification échoue avec "ES256" signatures must be "64" bytes : collez le jeton dans le débogueur JWT pour voir la longueur de la signature, et corrigez-la avec le convertisseur DER ⇄ brut.
Deux valeurs par défaut surprennent beaucoup d’équipes, l’une plus stricte et l’autre plus laxiste qu’elles ne le pensent : clockTolerance vaut zéro, si bien que quelques secondes de dérive d’horloge entre serveurs suffisent déjà à produire jwt expired, et exp est facultatif, de sorte qu’un jeton émis sans ce claim n’expire jamais. Définissez expiresIn à la signature, et maxAge ou une exigence explicite d’exp à la vérification ; vérifiez les secrets HMAC avec le vérificateur de secrets JWT.