Access Management 7.3.2

Backchannel request grant

Use the backchannel request grant for client-initiated backchannel authentication (CIBA).

CIBA lets a relying party (RP), the consumption device, get an end user’s consent without redirection through the end user’s browser. Instead, the end user authenticates and grants consent through an authentication device such as an authenticator application or a mobile banking application on the user’s mobile phone.

AM applies the guidelines suggested by the OpenID Financial-grade API (FAPI) Working Group to implement CIBA.

The backchannel flow

oidc-ciba
  1. The RP has a user identifier and requires the end user’s consent. It prepares a signed Json Web Token (JWT).

  2. The RP sends an HTTP POST request with the signed JWT to AM, the OpenID provider (OP).

  3. The OP validates the signature using the RP’s public key and verifies the JWT. If the JWT is valid, the OP returns an auth_req_id and a polling interval.

  4. The RP polls the OP with the auth_req_id, waiting for the end user’s authorization. If the RP does not respect the polling interval, the OP returns an error.

  5. The OP sends a push notification with the binding_message to request the end user’s authorization.

  6. The end user authorizes the request with the authorization gesture on their authentication device; for example, the user clicks a button in their authenticator application or provides their fingerprint.

  7. The OP returns an access token and an ID token to the RP.

    The RP can use the ID token subject ID claim as the end user’s identity.

  8. If the RP requires additional claims, it sends a request to the /oauth2/userinfo endpoint with the access token for authorization.

  9. If the access token is valid, the /oauth2/userinfo endpoint returns any additional claims.

    The RP can use the subject ID and the additional claims to identify the end user.

Prepare for CIBA

Configure the service

  1. Create a journey such as the following:

    The journey requires specific authentication nodes for CIBA.

    The journey uses these nodes:

    For details, refer to Push authentication journeys.

  2. In the AM admin UI, go to Realms > Realm Name > Services > OAuth2 Provider > Advanced and make sure the Grant Types field includes Back Channel Request.

    Save any changes you make.

  3. Associate the journey with incoming acr_values:

    1. Switch to the Advanced OpenID Connect tab of the OAuth 2.0 provider configuration.

    2. In the OpenID Connect acr_values to Auth Chain Mapping box, set the Key to acr_values, set the Value to the name of your journey, and click Add.

    3. Save your changes.

    For more information, refer to The acr claim.

Register an RP

  1. Register the RP as a confidential client application with the following settings:

    Name

    <human-readable-name>

    Client ID

    <rp-client-id> (must match the iss in the RP’s signed JWTs)

    Client Secret

    <rp-client-secret>

    Scopes

    openid
    profile

  2. Configure access to the RP’s public keys so AM can verify JWT signatures:

    1. On the Signing and Encryption tab, choose the Public key selector.

    2. Depending on the Public key selector value you chose, set one of the other fields appropriately.

    3. Save your changes.

    For example:

    • Set Public key selector JWKs_URI and Json Web Key URI to the URL where the RP publishes its keys.

      This method simplifies key rotation as AM rereads the keys periodically.

    • Set Public key selector to JWKs and set Json Web Key to a JWK set similar to the following:

      {
        "keys": [
          {
            "kty": "EC",
            "use": "sig",
            "crv": "P-256",
            "kid": "myCIBAKey",
            "x": "m0CkpWpZyGu-FLRLjCGBVGC7Fwm5vGt8Lm3HhYU4ylg",
            "y": "U8NMtO-C2c3yhu2I_ApAELttmaittfPNPQaIJxvTCHk",
            "alg": "ES256"
          }
        ]
      }

      You can store more than one key in the JWK set.

Get an auth request ID

Follow these steps as RP to get a CIBA authentication request ID:

  1. Prepare a signed JWT with the required claims in the payload:

    Claim Description Example

    acr_values

    A string identifying the mechanism for the end user to provide authorization.

    "acr_values": "push"

    aud

    A string or array of strings indicating the intended audience of the JWT. Must include the authorization server OAuth 2.0 endpoint.

    "aud": "https://openam.example.com:8443/openam/oauth2"

    binding_message

    A short (100 character max.) string message to display to the user when obtaining authorization.

    For push notification, messages must:

    • Begin with a letter, number, or punctuation mark.

    • Not include line breaks or control characters.

    "binding_message": "Allow ExampleBank to transfer £50 from 'Main' to 'Savings'? (EB-0246326)"

    exp

    The expiration time in seconds since January 1, 1970 UTC. An expiration time more than 30 minutes in the future causes a JWT expiration time is unreasonable error message.

    "exp": 1675681183

    id_token_hint

    An ID token identifying the principal and subject of the JWT (the end user).

    Required when not using login_hint.

    "id_token_hint": "<id-token>"

    iss

    The unique identifier of the JWT issuer; must match the client ID in the application profile.

    "iss": "myCIBAClient"

    login_hint

    A string identifying the principal and subject of the JWT (the end user).

    Required when not using id_token_hint.

    "login_hint": "a0325ea4-9d9b-4056-931b-ab64704cc3da"

    scope

    A string holding a space-separated list of the requested scopes; must include openid.

    "scope": "openid profile"

    For example:

    {
      "aud": "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha",
      "binding_message": "Allow ExampleBank to transfer £50 from 'Main' to 'Savings'? (EB-0246326)",
      "acr_values": "push",
      "exp": 1675681183,
      "iss": "<rp-client-id>",
      "login_hint": "<end-user-id>",
      "scope": "openid profile"
    }

    AM ignores keys specified in JWT headers, such as jku and jwe and uses the keys specified in the RP profile to verify the JWT signature.

  2. Send an HTTP POST to the /oauth2/bc-authorize endpoint with the signed JWT in the payload:

    $ curl \
    --request POST \
    --user '<rp-client-id>:<rp-client-secret>' \
    --data 'request=<signed-jwt>' \
    'https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/bc-authorize'

    AM returns a JSON object with the auth_req_id value:

    {
      "auth_req_id": "<auth-req-id>",
      "expires_in": 600,
      "interval": 2
    }

    AM sends a push notification with the binding_message to the end user.

Exchange an auth request ID for tokens

To get an access token and ID token as the RP, poll the /oauth2/access_token endpoint with HTTP POST requests having the following parameters:

  • grant_type=urn:openid:params:grant-type:ciba

  • auth_req_id=<auth-req-id>

For example:

$ curl \
--request POST \
--user '<rp-client-id>:<rp-client-secret>' \
--data 'grant_type=urn:openid:params:grant-type:ciba' \
--data 'auth_req_id=<auth-req-id>' \
'https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token'

The response depends on the end user and the polling interval:

  • After the end user has authorized the operation, AM returns an ID token and an access token:

    {
      "access_token": "<access-token>",
      "refresh_token": "<refresh-token>",
      "scope": "openid profile",
      "id_token": "<id-token>",
      "token_type": "Bearer",
      "expires_in": 3599
    }
  • Before the end user authorizes the operation, AM returns an HTTP 400 Bad Request status:

    {
      "error_description": "End user has not yet been authenticated",
      "error": "authorization_pending"
    }
  • The auth ID response includes a polling interval. The RP must wait interval seconds before retrying the request (default: two seconds). If the RP does not wait long enough between retries, AM returns an HTTP 400 Bad Request status:

    {
      "error_description": "The polling interval has not elapsed since the last request",
      "error": "slow_down"
    }

Additional OIDC claims

An RP can request additional claims about the end user with the access token at the /oauth2/userinfo endpoint:

$ curl \
--request GET \
--header "Authorization Bearer <access-token>" \
"https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/userinfo"
{
  "name": "<end-user-display-name>",
  "family_name": "<end-user-family-name>",
  "given_name": "<end-user-given-name>",
  "sub": "<end-user-id>",
  "subname": "<end-user-id>"
}