JWT token revocation by the resource server is an additional security element.
Any token which is no longer needed despite still being valid must be revoked. This prevents users from maliciously identifying themselves to a service.
The revocation operation is performed via an explicit request to a dedicated web API.
Adelia Studio offers two token revocation implementations:
- Simple implementation for a single resource server.
- Distributed implementation (several resource servers and/or third-party applications need to be informed of revoked tokens). This implementation is based on the Nats/Jetstream open source message broker.
Configuring revocation management
Revocation management is not enabled by default. To make it operational, a JwtRevokeConfiguration-type bean needs to be declared in the Beans.xml file.
Simple Mode
<bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap" /> or <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>
jwtClaimId: List of claim names able to identify the JWT. Default value: "jti". This information is mandatory: when revocation management is enabled, token validation - identified by its ID - is dependent on it not being revoked.
Note:
The token is identified by the first claim name in the valid list for the token. If the token does not have a valid claim for at least one of the names designated by the jwtClaimId property, token validation will fail; a 401 error will therefore be returned by the server.- jwtPurgeFrequencyTime: Purge frequency, in seconds, of the revoked token table. Default value: 3600. To be adapted according to the token validity period.
Distributed mode
In this mode, each revocation request issues a message in the ${jwtNatsSubject} subject of the ${jwtNatsStreamName} stream. The messages are kept in the stream for the period defined by ${jwtNatsStreamMaxAge}.
The message is a string in UTF-8 format comprising four fields delimited by the ; character. → Example: <JwtId>;<RevokedBy>;<RevocationRequestDate>;<TokenExpirationDate>
- <JwtId>: String> → Token Id
- <RevokedBy>: String> → Name of user initiating the revocation ( token 'sub' used during revocation)
- <RevocationRequestDate>: String; ISO-8601 → Revocation request date.
- <TokenExpirationDate>: Length → Scheduled token expiry date (unix time).
<bean id="jwtTokensRevocation" class="com.hardis.adelia.webservice.JwtRevokeConfiguration" init-method="loadTokensMap" destroy-method="saveTokensMap"> <property name="jwtClaimId" value="???"/> <!-- default value: jti --> <property name="jwtNatsServers" value="localhost:4222"/> <!-- if one or more servers are defined, shared mode is enabled provided there is a successful connection to one of the servers --> <property name="jwtNatsStreamName" value="ADELIASTREAM"/> <!-- Dedicated stream name; default: "ADELIASTREAM" --> <property name="jwtNatsSubject" value="streaming.adelia.internal.jwt.revoke"/> <!-- Subject name; default: "streaming.adelia.internal.jwt.revoke" --> <property name="jwtNatsStreamMaxAge" value="12"/> <!--in hours, maximum age of messages in the stream - value to be set in relation to the token lifespan; default: "24"--> <!-- Nats authentication : nkey --> <property name="jwtNatsAuthNKeySeed" value="CRYPT(00D133D32D285115D308A5E89F9CEA80D227FDB3304CBD18AD596D09AA40359D7F3DA0F74CCDE20B8A6BD53687A2584851712091A26207A7F7D790DD719753E7D7)"/> </bean>
jwtClaimId: List of claim names able to identify the JWT. Default value: "jti". This information is mandatory: when revocation management is enabled, token validation - identified by its ID - is dependent on it not being revoked.
Note:
The token is identified by the first claim name in the valid list for the token. If the token does not have a valid claim for at least one of the names designated by the jwtClaimId property, token validation will fail; a 401 error will therefore be returned by the server.- jwtNatsServers: Name of Nats server or names of Nats servers (separated by a ; → example: srvnats1:4222;srvnats2:4222).
- jwtNatsStreamName: Name of dedicated stream [default: ADELIA_JWT_TOKEN_STREAM].
- jwtNatsSubject: Name of subject used to revoke tokens [default: streaming.adelia.internal.jwt.revoke].
- jwtNatsStreamMaxAge: Maximum age of messages in the dedicated stream. This age must be greater than the token lifespan so that a new client/subscriber is aware of all the revoked tokens that are possibly active.
Nats server authentication
User/Password
<property name="jwtNatsAuthUser" value="bob"/><property name="jwtNatsAuthPwd" value="s3cr3t"/>
jwtNatsAuthUser: User's name
jwtNatsAuthPwd: Password. Use the Adelia runtime "java EncryptPassword" command to encrypt the password and specify the password in brackets, prefixed by "CRYPT".
→ Example:
<property name="jwtNatsAuthUser" value="bob"/>
<property name="jwtNatsAuthPwd" value="CRYPT(0040B783FDB007E032)"/>- Token
jwtNatsAuthToken: To encrypt the token, use the "java EncryptPassword" Adelia runtime command and specify the password in brackets prefixed by "CRYPT".
→ examples:
<property name="jwtNatsAuthToken" value="s3cr3t"/>
<property name="jwtNatsAuthToken" value="CRYPT(0040B783FDB007E032)"/> - Nkey
jwtNatsAuthNKeySeed: private key. To encrypt the key, use the "java EncryptPassword" Adelia runtime command and specify the password in brackets prefixed by "CRYPT".
→ examples:
<property name="jwtNatsAuthNKeySeed" value="SUAFGCEVJB6DTYFPEQIC4DSLGBRSD75BMQPO2HS6SX4FNPVZDTHMQYYN24"/>
<property name="jwtNatsAuthNKeySeed" value="CRYPT(00D133D32D285115D308A5E89F9CEA80D227FDB3304CBD18AD596D09AA40359D7F3DA0F74CCDE20B8A6BD53687A2584851712091A26207A7F7D790DD719753E7D7)"/> - Credentials file
jwtNatsAuthCredsFile: location of the file containing the authentication information. See https://docs.nats.io/using-nats/developer/connecting/creds
→ example:<property name="jwtNatsAuthCredsFile" value="nats.creds"/>
Who can revoke a token?
The token can only be revoked by a request authenticated by the same token.
Revoked token table persistence
The revoked token table remains after the resource server has been restarted.
Revoked token table purge
The revoked token table is purged at a specific frequency (every hour by default. See jwtPurgeFrequencyTime). Revoked tokens which have expired are purged. ↑ Top of page
Web APIs
Objective | API access point | HTTP verb | Response | Status |
Revocation | /<ContextPath>/<CXF_url-pattern>/tokens/revocation | @DELETE | Media-type: text/plain | 200 |
Is the jwtId token revoked? | /<ContextPath>/<CXF_url-pattern>/tokens/revocation/{jwtId} | @GET | Media-type: text/plain | 200 |
List of revoked tokens | /<ContextPath>/<CXF_url-pattern>/tokens/revocation/list | @GET | Media-type: application/json | 200 |
HTTP Code:
If the operation is successful: HTTP Code = 200
If the operation fails because the revocation request is not authorized: HTTP Code = 401
If the JwtRevokeConfiguration bean is not declared (= revocation not active) or if the requested token is not found: HTTP Code = 404.
EXECUTE_HTTP *URL('http://mysite/mawebapp/ws/tokens/revocation') *OPTIONS('--request DELETE --header Accept: text/plain --header Authorization:JWT <jwt_token>') *RESPONSE(VarRetCode) *HTTP_CODE(VarHttpCode) Note: "<jwt_token>" corresponds to the token value