Cervantes met en œuvre la spec MicroProfile JWT 2.1, qui s’appuie sur la suite JOSE de l’IETF : JWT (RFC 7519), JWS (RFC 7515), JWK (RFC 7517), JWE (RFC 7516), JWA (RFC 7518). Cette page récapitule le vocabulaire — claims, signature, kid, algorithm, JWK Set, audience — et la mécanique de confiance qui permet la vérification hors-ligne (sans appel à l’émetteur).
Le JWT — structure en trois parties
Un JWT compact est une chaîne de trois segments séparés par des points :
<header-base64url>.<payload-base64url>.<signature-base64url>
-
Header — JSON décrivant l’algorithme et la clé :
{"alg":"RS256","typ":"JWT","kid":"abc-123"}. -
Payload — JSON portant les claims :
{"iss":"…","aud":"…","exp":…,"groups":["…"]}. -
Signature — calculée sur
header + "." + payload, vérifiée par la clé publique de l’émetteur.
Encodage : Base64url sans padding (Base64.getUrlDecoder() du JDK). Aucune lib tierce nécessaire.
|
Une variante encore plus opaque : le JWE (RFC 7516) à cinq segments — header chiffré, clé enveloppée, IV, payload chiffré, tag d’authentification. Cervantes le déchiffre vers un JWS, puis valide normalement (voir plus bas). |
Claims standards (RFC 7519)
| Claim | Rôle |
|---|---|
|
Issuer — qui a émis le token. Comparé à |
|
Subject — identifiant de l’utilisateur (souvent un UUID, parfois un |
|
Audience — destinataire visé. Comparé à |
|
Expiration — timestamp Unix (secondes). Refus si |
|
Not Before — timestamp Unix (secondes). Refus si |
|
Issued At — timestamp Unix d’émission. Sert à |
|
JWT ID — identifiant unique du token (utile pour blocklist applicative). |
Claims MicroProfile JWT 2.1
| Claim | Rôle |
|---|---|
|
User Principal Name — devient |
|
Repli pour |
|
Liste de chaînes — les rôles adossés à |
|
Pseudo-claim — quand on injecte |
L’interface org.eclipse.microprofile.jwt.JsonWebToken expose tous ces claims via getClaim(String), getClaimNames(), getName(), getGroups().
Signature JOSE — algorithmes supportés
Cervantes supporte les familles couvertes par la spec MP JWT 2.1 et par le JDK, sans dépendance externe.
| Famille | Algorithmes | Notes |
|---|---|---|
RSA (signature) |
|
|
RSA-PSS |
|
Variante probabiliste de RSA (RFC 8017). |
ECDSA |
|
Courbes P-256 / P-384 / P-521. Transcodage interne JOSE R‖S ↔ DER (le JDK exige DER, JOSE exige R‖S concaténés). |
HMAC |
|
Clé partagée (symétrique). Hors profil MP par défaut — utilisable en interne. |
mp.jwt.verify.publickey.algorithm impose un algorithme attendu. Si le header du token annonce un autre alg, la validation échoue. Défense en profondeur contre les attaques alg=none ou la confusion RS256/HS256.
JWK Set — distribution des clés publiques
Un JWK Set (RFC 7517) est un document JSON publié par l’émetteur :
{
"keys": [
{
"kty": "RSA", "use": "sig", "alg": "RS256",
"kid": "abc-123",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e": "AQAB"
}
]
}
Cervantes :
-
Charge le JWK Set au démarrage (paresseusement à la première validation, en réalité — pas de blocking startup).
-
Le met en cache avec un TTL.
-
Sélectionne la clé via le
kiddu header du token. -
Si le
kidn’est pas trouvé, déclenche un refresh borné parminRefreshInterval(pour résister à un déni de service parkidinconnu). -
En cas d’échec réseau, retombe sur le snapshot précédent.
JwksKeyResolver implémente cette logique. Voir Internals pour le détail thread-safe (ReentrantLock + AtomicReference).
Rotation de clés
Pour ne pas invalider tous les tokens existants quand l’émetteur change de clé :
-
L’émetteur publie deux clés dans son JWK Set : l’ancienne et la nouvelle, chacune avec son
kid. -
Les nouveaux tokens sont signés avec la nouvelle clé (
kidnouveau). -
Cervantes voit un
kidqu’il ne connaît pas, refresh le JWK Set, trouve la clé, valide. -
Quand tous les tokens en circulation portent le nouveau
kid, l’émetteur peut retirer l’ancienne clé du JWK Set.
Aucune coordination distribuée n’est nécessaire : la rotation est portée par le JWK Set, vue par cervantes lors du refresh.
JWE — tokens chiffrés (optionnel)
La spec MP JWT 2.1 §4 prévoit que le token transporté puisse être chiffré (JWE) plutôt que simplement signé (JWS). Cervantes le supporte :
-
Enveloppement de clé :
RSA-OAEP,RSA-OAEP-256. Clé privée du destinataire (mp.jwt.decrypt.key/.location). -
Chiffrement de contenu :
A256GCM(défaut spec).A128CBC-HS256n’est pas encore supporté — à ajouter si requis. -
Détection : un JWE a 5 segments.
DefaultJwtValidatordistingue JWS (3 segments) et JWE (5 segments) et déchiffre avant validation. -
Conformité spec : le header JWE doit porter
cty: JWTquand le contenu est lui-même un JWT (nested).
Voir Référence pour la liste des clés mp.jwt.decrypt.*.
Modèle de confiance — pourquoi c’est vérifiable hors-ligne
Le contrat JWT est : je te fais confiance pour vérifier ma signature avec ma clé publique, je ne fais confiance à personne d’autre. Cervantes incarne ce contrat :
-
L’émetteur signe avec sa clé privée. Personne d’autre ne peut produire un token qui passe la vérif.
-
Le service (votre application, propulsée par Cervantes) connaît la clé publique de l’émetteur — par
mp.jwt.verify.publickeyou via le JWK Set publié à l'`iss`. -
La vérification est locale : aucun appel à l’émetteur par requête (sauf pour rafraîchir le JWK Set, et seulement si nécessaire).
-
Le temps est mesuré par le service (
Clock.systemUTC()par défaut), avec une tolérancemp.jwt.verify.clock.skew(défaut 60 s) pour absorber les dérives. -
Le rôle vient du claim
groups— c’est l’émetteur qui décide qui a quels rôles, et Cervantes l’accepte.
Le service de confiance se résume à : je vérifie une signature avec une clé publique connue, je vérifie des claims contre une configuration connue, je n’appelle personne.
Pour aller plus loin
-
Fonctionnement interne — pipeline détaillé, BCE Vauban, cache JWKS, threading.
-
Référence — annotations, clés
mp.jwt.*, artefacts. -
Cas d’usage — exemples complets.