Custom token response SPI

1. Overview

The OAuth 2.0 framework (RFC 6749) allows for additional parameters to be sent in the access token response. This is used for instance by OpenID Connect to return its ID token to the relying party and by the new Rich Authorisation Requests (RAR) to include metadata about the token's capabilities.

Example access token response which includes RAR metadata:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store
Pragma: no-cache

   "access_token": "2YotnFZFEjr1zCsicMWpAA",
   "token_type": "example",
   "expires_in": 3600,
   "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
   "authorization_details": [
         "type": "",
         "actions": [
         "locations": [
         "instructedAmount": {
            "currency": "EUR",
            "amount": "123.50"
         "creditorName": "Merchant123",
         "creditorAccount": {
            "iban": "DE02100100109307118603"
         "remittanceInformationUnstructured": "Ref Number Merchant"

If a Connect2id server deployment needs to return additional parameters in the token response it can do so by creating an SPI plugin.

2. Custom token response composer SPI

The creation of custom token success and error responses is facilitated via the CustomTokenResponseComposer (SPI) from the Connect2id server SDK.

Implementations have access to

If there is no need to modify the token response the plugin can simply pass it through.

If the Connect2id server detects an SPI implementation it will log its loading under OP6218.

INFO main MAIN - [OP6218] Loaded custom token response composer: class=com.nimbusds.openid.connect.provider.spi.tokens.response.impl.RARCustomizer enabled=true

3. Sample RAR plugin

This is a sample RAR plugin that obtains the content of the authorization_details from the optional authorisation data set during consent.

Token error responses and responses which have no associated RAR details are simply returned unmodified.

import net.minidev.json.JSONObject;
import com.nimbusds.oauth2.sdk.*;
import com.nimbusds.oauth2.sdk.util.*;
import com.nimbusds.openid.connect.provider.spi.tokens.response.*;

public class RARCustomizer implements CustomTokenResponseComposer {

    public TokenResponse compose(final TokenResponse originalResponse,
                                 final TokenResponseContext context) {

        if (originalResponse instanceof TokenErrorResponse) {
            return originalResponse;

        JSONObject authzData = context.getAuthorizationData();

        if (authzData == null) {
            // No data / RAR
            return originalResponse;

        JSONObject rarDetails;
        try {
            rarDetails = JSONObjectUtils.getJSONObject(
        } catch (ParseException e) {
            throw new RuntimeException("Internal error: " + e.getMessage(), e);

        if (rarDetails == null) {
            // No RAR
            return originalResponse;

        AccessTokenResponse successResponse = originalResponse.toSuccessResponse();

        try {
            JSONObject tokensObject = successResponse.toJSONObject();
            tokensObject.put("authorization_details", rarDetails);
            return AccessTokenResponse.parse(tokensObject);
        } catch (ParseException e) {
            throw new RuntimeException("Internal error: " + e.getMessage(), e);