OpenID Connect request mapping Open Banking intent ID

When a third-party application wants to initiate a transaction that requires user authorization (for example, payment transfer), it uses an API that is provided by the financial institution or bank to register the details of the transaction. For example, it registers the amount of the transaction and the type of currency. This process is sometimes referred to as "lodging an intent". The API responds with an intent identifier, also called a Consent ID in UK Open Banking specifications. The application initiates an OAuth authorization code flow to get user authorization that is recorded as a user consent.

Because the payload that represents the transaction and the process of including the intent ID in the authorization request is not well-defined, an administrator can use IBM® Security Verify to script the following actions by using a custom rule.
  • Extract the intent ID from the request.
  • Obtain the intent context information, usually by calling the bank's API.
  • Choose how to represent the authorization in the id_token as a claim or scope value.
The custom rule is based on the syntax that is described in Multi-line rule executor. Simpler rules can be single line and do not need to be written in the YAML format.

Available input objects

Because this custom rule is run after authentication but before authorization, the information that is available does not include all possible domain objects. They are limited to the following types.

HTTP request context
When a user logs in to Verify, the incoming HTTP request context can be accessed in the request-mapping rule. All OAuth request parameters, such as claims and scope, are available in this requestContext. The general structure and use of requestContext is described in the "HTTP Request Context" section of the Attribute functions.
For simplicity of access, certain values are precomputed in the requestContext. The claims request parameter is generally represented as a JSON like the following example.

{
    "id_token": { 
        "claim_name": { 
            "essential": false, 
            "value": "some_value"
        }
    },
    "userinfo": {
        "claim_name": { 
            "essential": false, 
            "value": "some_value"
        }
    }
}

This JSON can be cumbersome to access and so the key that is used in the requestContext is in the format claims_claimType_claimName. The claimType is either userinfo or idtoken. Note the missing underscore. Thus, with the previous example, the claim_name value can be obtained by using requestContext.getValue('claims_idtoken_claim_name').

Similarly, scope is computed and split by a space to build a string array.

Identity source credential

When a user logs in to Verify, the identity source credential attributes are added into the login session and can be accessed in the request-mapping rule.

The idsuser domain object is available as a map with a string key and a string array value. For example,

{
  "realmName": ["cloudIdentityRealm"],
  "displayName": ["Jessica J. Hill"],
  "phone": ["+12324321234"]
}
For more information, see Attribute functions.
Other functions and operators

Standard operators and functions are available in the mapping rule. The HTTP client is also available to make outbound requests. Other supported functions include hashing and timestamps. See the relevant sections in the Attribute functions.

Return object

Because this custom rule must be interpreted to build an authorization context, the return value of this rule must be a JSON object that must include the following mandatory properties:

Property Type Required? Description
type String Yes The type indicates the type of transaction that is being performed. For example, a payment initiation. This is represented in Verify as a Data Privacy Purpose. The purpose ID that is provided or generated by the system is the value of type.
intentID String Yes The intent ID must be supplied. It is usually computed from the requestContext.
claims JSON object. JSON property value can be of any type. No Typically, on user authorization, the ID token that is generated contains some indication that the user authorized the transaction that is identified by the intent ID. The claims is a JSON object or map, where the key is the claim name and the value can be any JSON-compatible structure, such as string, integer, another object, array, or other structures. Multiple claims can also be specified.
scope String No If the request is authorized, this value is added to the granted scopes.

Any additional attributes, such as the amount or currency is treated as custom attributes. They can be shown in the user consent page by using the corresponding template macros that are described later.

UK Open Banking example

To initiate a payment, an intent must be lodged first. The following participants are involved in this scenario:
  • A payment initiation service provider (PISP) that resides on the third-party provider and initiates the payment transaction.
  • An account Information service provider (ASPSP) authorization server. In this scenario, the ASPSP is Verify.
  • An ASPSP resource server that hosts the payment initiation API.
The PISP redirects the user to perform authorization against the authorization server, along with the intent ID. The authorization server needs to perform the following tasks:
  1. Extract the intent ID.
  2. Retrieve information about this transaction from the resource server.
This custom rule performs both of these operations.
Extracting the intent ID from the request
This example follows the UK Open Banking standard of sending the intent ID through the claims. The incoming authorization request contains the openbanking_intent_id in the claims.

{
	"id_token": {
		"openbanking_intent_id": {
			"value": "b508f9df-799b-4120-a13e-5d09f2931fa6",
			"essential": true
		}
	}
}
The claims are extracted and made available in the request context. The code that is used to retrieve the value is

requestContext.getValue('claims_idtoken_openbanking_intent_id')
Retrieve transaction information
Transaction information is stored in the Account Servicing Payment Service Provider (ASPSP) resource server, which is where the consent or intent ID was generated. Use the HTTP client to make a request to the resource server. The following request is an example.
hc.getAsJSON("https://resource.myaspsp.com/internal/intents/" + context.intentID
Map transaction to a privacy purpose and claims
To capture Open Banking payment initiation and account access consents, relevant privacy purposes must be created. For example, a payment_initiation privacy purpose must be created for any payment initiation consent requests. This purpose is then mapped to the type property in the return object.
Additionally, for UK Open Banking, an openbanking_intent_id claim needs to be created. The remaining information about the transaction can also be added to the return object.

{
   "type": "payment_initiation",
   "intentID": "b508f9df-799b-4120-a13e-5d09f2931fa6",
   "currency": "USD",
   "amount": "1200.35",
   "merchant": "Merchant A",
   "claims": {
      "openbanking_intent_id": "b508f9df-799b-4120-a13e-5d09f2931fa6"
   }
}
Sample mapping rule

statements:
- if:
    match: "!has(requestContext.claims_idtoken_openbanking_intent_id)"
    return: null
- context: "intentID := requestContext.getValue('claims_idtoken_openbanking_intent_id')"
- context: 'intentContext := hc.getAsJSON("https://resource.myaspsp.com/internal/intents/" + context.intentID, { "Authorization": "apikey supersecretapikey" })'
- return: >-
    {
        "type": context.intentContext.type,
        "intentID": context.intentID,
        "currency": context.intentContext.instructedAmount.currency,
        "amount": context.intentContext.instructedAmount.amount,
        "merchant": context.intentContext.creditorName,
        "claims": {
            "openbanking_intent_id": context.intentID
        }
    }

Customizing the consent page

Because the consent user experience for transaction authorization differs from standard OAuth and Open ID Connect scope consent, a custom section can be created in the consent page template, see Modify single sign-on for OpenID pages.

For example,

[RPT purpose_payment_initiation]
<input type="hidden" name="@PRIVACY_SCOPE_PNAME_REPEAT@"
    value="@PRIVACY_SCOPE_REPEAT@" privacy-readonly="true" />
<input type="hidden" id="@PRIVACY_SCOPE_PNAME_REPEAT@_state" name="@PRIVACY_SCOPE_PSTATE_REPEAT@"
    value="CONSENT_ALLOW" privacy-required="@PRIVACY_SCOPE_REQUIRED_REPEAT@" />
<div id="@PRIVACY_SCOPE_PNAME_REPEAT@_widget" class="bx--form-item" style="width:350px;"></div>
<div class="bx--grid" style="padding-left:0">
    <div class="bx--row" style="padding-bottom:6px;">
    <div class="bx--col-sm-1">Amount</div>
    <div class="bx--col">@PRIVACY_SCOPE_CUSTOM_currency@ @PRIVACY_SCOPE_CUSTOM_amount@</div>
    </div>
    <div class="bx--row" style="padding-bottom:6px;">
    <div class="bx--col-sm-1">Merchant</div>
    <div class="bx--col">@PRIVACY_SCOPE_CUSTOM_merchant@</div>
    </div>
    <div class="bx--row" style="padding-bottom:6px;">
    <div class="bx--col-sm-1">Reference</div>
    <div class="bx--col">@PRIVACY_SCOPE_ATTRVALUE_REPEAT@</div>
    </div>
</div>
[ERPT purpose_payment_initiation]
Notice the following aspects.
  • A new repeatable section that is used for a specific Data Privacy purpose. The name that is used follows the format purpose_{purposeID}. The purposeID is the Data Privacy purpose ID. This purpose ID can be system-generated or user-provided.
  • Two HTML Form fields are essential and are named @PRIVACY_SCOPE_PNAME_REPEAT@ and @PRIVACY_SCOPE_PNAME_REPEAT@_state. The first field contains the Verify identifier for the consent record. The second field contains the state of consent.
  • Custom attributes that are returned by the advanced rule can be referenced by using the macro @PRIVACY_SCOPE_CUSTOM_{attributeName}@. For example, @PRIVACY_SCOPE_CUSTOM_currency@ is replaced by the currency value that is returned in the intent advanced rule.