ID Token Blacklist#
As described in Deauthentication, an authenticated person can invalidate a previously issued ID token. Future use of the token is barred by including its unique transaction ID in a blacklist. Since tokens have a natural expiration time, they remain on the blacklist only until they would have expired.
The information about a blacklisted token must be transported reliably to other backend applications which rely on ID tokens. This page describes this process.
Concept#
High level view, using Skalio ID and Spaces as backend applications:
- Both Skalio ID and Spaces use a database for persistence of objects. It is important to note though that these are separate databases.
- Each application implements an independent blacklist repository (in its database).
- Spaces is registered as a known backend application.
- Both Skalio ID and Spaces connect to the same message broker (cluster). This is in fact their only way of direct interaction with each other.
Process:
- [Spaces, broker] At application startup, a message listener subscribes to the blacklist queue. It uses a message selector, filtering on
target
. - [Skalio ID] Person sends a logout request and deauthenticates a token.
- [Skalio ID] Authentication and authorization is confirmed.
- [Skalio ID, database] The corresponding entry for the issued token is removed.
- [Skalio ID, database] An entry into the blacklist is made for the transaction ID.
- [Skalio ID, broker] For each registered backend application, Skalio ID publishes a message on the broker, containing the blacklisted token and its expiration time. Messages are published on the same blacklist queue, but with individual values in header
target
. - [broker, Spaces] The message is delivered to the message listener.
- [Spaces, database] An entry into the blacklist is made for the transaction ID.
- [Spaces] A request authenticated with an ID token is rejected after detecting it on the local blacklist.
is used.
The values for target
-header are taken from
- Skalio ID: the configuration parameter
feature.enabledBackendApplications
- other applications: the configuration value
IBaseConfiguration#getProgramName()
Implementation#
Blacklist repository#
Each participating backend application must provide a blacklist repository by implementing com.skalio.auth.ITokenBlacklist
as a @Service
.
The repository provides at least the following two methods:
/**
* Records the token into the local blacklist.
* <p>
* May also trigger other local actions, like publishing of the token cluster wide.
*
* @param token
*/
void blacklist(BlacklistedToken token);
/**
* Returns true if the token is blacklisted, false otherwise.
*
* @param jti
* @return
*/
boolean exists(String jti);
Message listener#
skp-common provides the com.skalio.auth.BlacklistSyncListener
, a runlevel service that runs automatically at level 3. During startup it attempts to locate an implementation of the ITokenBlacklist
and terminates if it doesnt find one.
The listener subscribes by default to the queue com.skalio.cluster.tokenBlacklist
. It uses a message selector to filter incoming messages. It filters messages with JMS header target ='<programName>'
. The value is taken from IBaseConfiguration#getProgramName()
.
The BlacklistSyncListener
handles messages by unwrapping a com.skalio.auth.BlacklistedToken
and passing it to the implementation of ITokenBlacklist#blacklist(token)
.
As a result, the backend application must only implement the blacklist repository to participate in cluster wide blacklist synchronisation.
Publishing a blacklisted token#
When a person deauthenticates a token, the Skalio ID AuthLogoutResource
calls ITokenBlacklist#blacklist(token)
. The implementation records the token in its repository, but also publishes it on the broker in queue com.skalio.cluster.tokenBlacklist
.
For each registered backend application, a separate message is issued with its own target
message header. The list of possible values is taken from IFeatureConfiguration#getEnabledBackendApplications()
. This maps to the configuration setting feature.enabledBackendApplications
, containing a semicolon-separated list of program names of the backend applications. Example:
feature.enabledBackendApplications=spaces-backend;
Messages are marked as persistent, with a time-to-live equal to the expiration time of the token.
ID token validation#
A jersey resource can activate a request-filter to ensure that requests are authenticated with a valid ID token. This is achieved by annotation the resource with @RequireValidIDToken
. This annotation ties the request to the IDTokenAuthFilter
, which aborts requests, if token validation fails.
Process:
- search for bearer token in HTTP header
Authorization
- verify and decode token
- check blacklist for
jti
of token - ensure token identifies a subject
- build
com.auth.SkalioPrincipal
from decoded token - populate security context of request with the principal
If any of the steps fails, it aborts the request with an UnAuthorizedException
.
To check the blacklist, the IDTokenAuthFilter
attempts to locate an implementation of the ITokenBlacklist
contract. If found, it calls ITokenBlacklist#exists(jti)
on it.