La révocation de jetons JWT par le serveur de ressources (application exposant les webapis REST) est un élément de sécurisation.
Un jeton dont l'utilité est devenue caduque alors que sa durée de validité n'est pas révolue doit être révoqué. Ainsi il ne peut être utilisé pour s'authentifier malicieusement auprès d'un service.
L'opération de révocation est réalisée par le biais d'une requête explicite à une webapi dédiée.
Note/mise en garde : l'implémentation de la révocation des jetons par le serveur de ressources casse le paradigme d’une application stateless scalable. Les informations de révocation ne sont pas partageables entre deux instances de l'application.
La gestion de la révocation n'est pas active par défaut. Pour la rendre opérationnelle, il faut déclarer - dans le fichier Beans.xml - un bean de type JwtRevokeConfiguration
<bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap" /> ou <bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap"> <property name="jwtClaimId" value="???"/> <!-- default value: jti --> <property name="jwtPurgeFrequencyTime" value="???"/> <!-- default value: 3600 --> </bean> |
Purpose | Api Endpoint | HTTP Verb | Response | Status |
Revocation
| /<ContextPath>/<CXFServletPath>/tokens/revocation | @DELETE | Media-type : text/plain | 200 |
Is a token revoked ?
| /<ContextPath>/<CXFServletPath>/tokens/revocation/{jwtId}
| @GET | Media-type : text/plain
| 200 |
Revoked tokens list | /<ContextPath>/<CXFServletPath>/tokens/revocation/list
| @GET | Media-type : application/json | 200
|
HTTP Code :
Si l’opération aboutit : HTTP Code = 200
Si l’opération échoue car la requête de révocation n’est pas autorisée : HTTP Code = 401
Si le bean JwtRevokeConfiguration n’est pas déclaré (= révocation non active) ou si le jeton interrogé n’est pas trouvé : HTTP Code = 404.
Ajout d'une implémentation "distribuée" de la révocation des jetons JWT à l'aide de NATS/JetStream.
Si au moins un serveur Nats est défini et qu'une connexion à celui-ci est établie alors le mode distribué est activé (sous condition de la création réussie ou de l'existence du stream et du sujet).
Dans ce mode chaque demande de révocation émet un message dans le sujet ${jwtNatsSubject} du stream ${jwtNatsStreamName}. Les messages sont conservés dans le stream pour la durée définie par ${jwtNatsStreamMaxAge}.
Le message est une chaine au format UTF-8 composée de 4 champs délimités par le caractère ; → Exemple : <JwtId>;<RevokedBy>;<RevocationRequestDate>;<TokenExpirationDate>
<bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap"> <property name="jwtNatsServers" value="localhost:4222"/> <!-- si un ou plusieurs serveurs sont définis alors le mode partagé est activé sous condition d'une connexion réussie à l'un des serveurs --> <property name="jwtNatsStreamName" value="ADELIASTREAM"/> <!-- Nom du stream dédié ; défaut : "ADELIASTREAM" --> <property name="jwtNatsSubject" value="streaming.adelia.internal.jwt.revoke"/> <!-- Nom du sujet ; défaut : "streaming.adelia.internal.jwt.revoke" --> <property name="jwtNatsStreamMaxAge" value="12"/> <!--en heures, âge maximal des messages dans le stream - valeur à fixer au regard de la durée de vie des jetons ; défaut: "24"--> <!-- Nats authentication : nkey --> <property name="jwtNatsAuthNKeySeed" value="CRYPT(00D133D32D285115D308A5E89F9CEA80D227FDB3304CBD18AD596D09AA40359D7F3DA0F74CCDE20B8A6BD53687A2584851712091A26207A7F7D790DD719753E7D7)"/> </bean> |
Authentification au serveur Nats :
User/Password
jwtNatsAuthUser : Nom de l'utilisateur
jwtNatsAuthPwd : Mot de passe. Pour chiffrer le mot de passe utilisez la commande "java EncryptPassword" du runtime Adélia, et indiquez le mot de passe entre parenthèses préfixé par "CRYPT".
→ exemples :
jwtNatsAuthCredsFile : emplacement du fichier contenant les informations d'authentification. Cf/ https://docs.nats.io/using-nats/developer/connecting/creds
→ exemple :Possibilité de préciser une liste de noms de claim pour la propriété jwtClaimId : utiliser le ; comme séparateur.
Le jeton est identifié par le premier nom de claim de la liste valide pour le jeton.
<bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap"> ... <property name="jwtClaimId" value="jti;tid"/> <!-- liste des noms de claim pouvant identifier le jeton --> ... </bean> |