Access token encoding and introspection SPIs

1. Overview

The Connect2id server can issue access tokens with two kinds of encodings:

  • Self-contained -- The authorisation is encoded in a JSON Web Token (JWT), which a resource server can validate locally by checking its digital signature.

  • Identifier-based -- A high-entropy key to an authorisation record in the Connect2id server database. The resource server validates the key and retrieves the associated data with a call to the introspection endpoint.

Deployments are in most cases fine with the provided access token encodings and introspection output provided by the Connect2id server. If some customisation is necessary the server exposes three plugin interfaces (SPIs) for that:

A context parameter is passed with each call so that the cryptographic facilities (e.g. JWS signer) and the OpenID claims source of the Connect2id server can be accessed if needed.

The access token SPIs were introduced in Connect2id server v6.17.

2. Self-contained access token claims codec

This SPI determines how a token authorisation is converted to a JWT claims set and vice versa. You can use it to set an alternative claims encoding to those provided by the Connect2id server.

A custom codec may be needed when the resource servers expect a different name or format for some non-standard JWT claim, such as the claim to represent the token scope or the client to which the token was issued. In that case extend the provided BaseSelfContainedAccessTokenClaimsCodec class.

@Override
public JWTClaimsSet encode(final AccessTokenAuthorization tokenAuthz, final TokenEncoderContext context) {

    // Encode the claims for which we have standard (registered) names
    JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder(super.encode(tokenAuthz, context));

    // client_id
    if (tokenAuthz.getClientID() != null)
        builder.claim("client_id", tokenAuthz.getClientID().getValue());

    // scope
    if (tokenAuthz.getScope() != null)
        builder.claim("scope", tokenAuthz.getScope().toString());

    return builder.build();
}


@Override
public AccessTokenAuthorization decode(final JWTClaimsSet claimsSet, final TokenCodecContext context)
    throws TokenDecodeException {

    // Decode the claims for which we have standard (registered) claims
    MutableAccessTokenAuthorization authz = new MutableAccessTokenAuthorization(super.decode(claimsSet, context));

    String clientID = claimsSet.getStringClaim("client_id");

    if (clientID != null)
        authz.withClientID(new ClientID(clientID));

    String scope = claimsSet.getStringClaim("scope");

    if (scope != null && ! scope.trim().isEmpty())
        authz.withScope(Scope.parse(scope));

    return authz;
}

Note that the default codec supports the inclusion of custom parameters via the top-level data ("dat") claim, with the option to make selected ones top-level JWT claims with help of the authzStore.accessToken.codec.jwt.moveAuthzData configuration property.

FAQ:

  1. How can I verify that my codec was loaded?

    The Connect2id server logs the loaded codec class at startup under AS0511 at INFO level:

    MAIN - [AS0511] Loaded class com.nimbusds.authzstore.codecs.DefaultSelfContainedAccessTokenClaimsCodec
    
  2. How can I insert selected client registration (metadata) fields?

    The TokenCodecContext provides access to the metadata of the client. To include custom parameters from the client "data" field use the authzStore.accessToken.codec.jwt.copyClientData configuration property.

  3. My resource servers expect access tokens with different JWT profiles?

    A codec can implement multiple JWT profiles and switch between them based on token audience, a scope value or some other chosen parameter.

3. Identifier-access token codec

This SPI generates secure random identifiers for access tokens.

The default implementation provided by the Connect2id server produces an 128-bit identifier for each token. The identifier has an extra SHA-256 HMAC protection (truncated to 128 bits) appended to it. During introspection the HMAC enables the codec to detect tokens that aren't issued by the Connect2id server, causing a suitable warning to get logged and preventing an unneeded database lookup.

The SPI enables plug in of a custom codec. For example, a codec where the identifier is encapsulated in a signed JWT along with the issuer URL, expiration time and client certificate confirmation, to provide resource servers in multi-tenant deployments with a hint where to introspect the token, and also allow part of the token validation to happen before the introspection call.

{
  "iss" : "https://t1.c2id.com",
  "jti" : "Hoofao7Ve1ohg4chahBee9Xee1ahvaed",
  "exp" : 1519660677,
  "cnf" : { "x5t#S256" : "Shoohie2Pee1ubi9aehai3leg0woidet" }
}

Check out this example for an alternative "hybrid" codec.

FAQ:

  1. How can I verify that my codec was loaded?

    The Connect2id server logs the loaded codec class at startup under AS0510 at INFO level:

    MAIN - [AS0510] Loaded class com.nimbusds.openid.connect.provider.spi.tokens.HybridIdentifierAccessTokenCodec
    
  2. The Connect2id server issued a JWT-encoded access token?

    The default encoding of issued access tokens by the Connect2id server is self-contained (JWT). To ask for an identifier-based token to be issued set the access_token.encoding parameter in your authorisation session handler. The password grant, client credentials and other grant handlers also assume self-contained access tokens by default.

4. Token introspection response composer

Resource servers check identifier-based access tokens at the standard token introspection endpoint.

The TokenIntrospectionResponseComposer SPI allows the default response format to be overridden.

Tailoring responses for multi-audience tokens

One potential use is to respond differently for a given multi-audience token depending on the identity of the resource server making the introspection request. For instance, an authorisation server may limit which scopes from a given token are returned for a resource server, to prevent it from learning more about the larger network than is necessary for its operation.

Claims source access

Plugins can access the claims source to retrieve user claims for inclusion in the token introspection response.

Use the TokenIntrospectionContext.getClaimsSource method for this.

Subject session access

Plugins can also access the subject session where the token issue was authorised, provided the session is still present (not closed or expired). A session presence check be used as a condition to return an active token introspection response. The session may also be used as a source of user claims, for example claims stored in the claims field when the user was authenticated.

Use the TokenIntrospectionContext.getSubjectSession method for this.

Since v14.0.

Sample plugin

A sample implementation of that can be found in the Connect2id server SDK test suite. Requires the resource server to be registered as a client to the Connect2id server (which is already a prerequisite to inspect tokens), where in the possible scope values that it may see are listed in the "scope" registration field.

FAQ:

  1. How can I verify that my response composer was loaded?

    The Connect2id server logs the loaded class at startup under OP6530 at INFO level:

    MAIN - [OP6530] Loaded class com.nimbusds.openid.connect.provider.spi.tokens.introspection.TokenScopeShaper