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
-
The RP has a user identifier and requires the end user’s consent. It prepares a signed Json Web Token (JWT).
-
The RP sends an HTTP POST request with the signed JWT to AM, the OpenID provider (OP).
-
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. -
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. -
The OP sends a push notification with the
binding_message
to request the end user’s authorization. -
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.
-
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.
-
If the RP requires additional claims, it sends a request to the /oauth2/userinfo endpoint with the access token for authorization.
-
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
-
Create a journey such as the following:
The journey uses these nodes:
For details, refer to Push authentication journeys.
-
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.
-
Associate the journey with incoming
acr_values
:-
Switch to the Advanced OpenID Connect tab of the OAuth 2.0 provider configuration.
-
In the OpenID Connect acr_values to Auth Chain Mapping box:
-
Set the Key to the value that will be passed in through the
acr_values
claim of the incoming CIBA request. -
Set the Value to the name of your journey.
-
Click Add.
-
-
Save your changes.
For more information, refer to The
acr
claim. -
Register an RP
-
Register the RP as a confidential client application with the following settings:
- Name
-
<human-readable-name>
- Client ID
-
<rp-client-id>
(must match theiss
in the RP’s signed JWTs) - Client Secret
-
<rp-client-secret>
- Scopes
-
openid
profile
-
Configure access to the RP’s public keys so AM can verify JWT signatures:
-
On the Signing and Encryption tab, choose the Public key selector.
-
Depending on the Public key selector value you chose, set one of the other fields appropriately.
-
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:
-
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
andjwe
and uses the keys specified in the RP profile to verify the JWT signature. -
-
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 waitinterval
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>"
}