JavaScript Mapping rule reference
JavaScript mapping rule
This document covers all the interfaces available for mapping rule JavaScript.
1. STSUniversalUser
The general rule of STSUniversalUser is that, the authenticated user claims are available under the AttributeContainer
section and request parameters are available under the ContextAttribute
section.
Return Type | Method | Description | Arguments |
---|---|---|---|
AttributeContainer | getAttributeContainer() | Return the AttributeContainer section. | |
AttributeContainer | getContextAttributes() | Return the ContextAttribute section. | |
addAttribute(attr) | Add attribute into AttributeContainer section. | Attribute | |
addContextAttribute(attr) | Add attribute into ContextAttribute section. | Attribute | |
getAttributeValueByName(name) | Retrieve attribute from AttributeContainer section. | string | |
setPrincipalName(name) | Set principal name. | string | |
string | getPrincipalName() | Retrieve principal name. | |
string | toString() | Stream STSUniversalUser to JSON string. |
1.1. Attribute
Return Type | Method | Description |
---|---|---|
string | getName() | Return attribute's name. |
string | getType() | Return attribute's type. |
any | getValue() | Return attribute's value. |
any[] | getValues() | Return attribute's values. |
1.2. AttributeContainer
Return Type | Method | Description | Arguments |
---|---|---|---|
Attribute | getAttributeByName(name) | Return attribute that matches the name that was provided. | string |
Attribute[] | getAttributeByType() | Return attributes that match the type that was provided. | string |
Attribute | getAttributeByNameAndType(name, type) | Return attribute that matches the name and type that were provided. | string, string |
any | getAttributeValueByName(name) | Return first value of the attribute that matches the name that was provided. | string |
any[] | getAttributeValuesByName(name) | Return all values of the attribute that match the name that was provided. | string |
any | getAttributeValueByNameAndType(name, type) | Return first value of the attribute that matches the name and type that were provided. | string, string |
any[] | getAttributeValuesByNameAndType(name, type) | Return all values of the attribute that match the name and type that were provided. | string |
any[] | setAttributeObject(attr) | Set attribute into the attribute container. | Attribute |
any[] | setAttribute(name, type, values) | Create an attribute in the attribute container with the value that was provided. | string, string, any |
Attribute | removeAttributeByNameAndType(name, type) | Remove attribute based on name and type that were provided. | string, string |
Boolean | removeAttribute(attr) | Remove the specified attribute from the container. | Attribute |
2. Client and definition
In the mapping rule javascript, Client and Definition objects
are available under oauth_client
and oauth_definition
, respectively.
3. PreToken mapping rule
Pre-token mapping rule is used to enrich the tokens, meaning that it produces claims that are output in the id_token
or /userinfo
endpoint.
It also produces claims that are presented when tokens are introspected at the /introspect
endpoint.
This approach is a slightly different than what is in ISVA.
In the JavaScript context, two JS map objects are displayed: tokenData
and idtokenData
.
tokenData
and idtokenData
are global variables available in the mapping rule, they are used for the purpose of grant enrichment.
tokenData
is used to add claims to the /introspect
response, essentially the access token.
idtokenData
is used to add claims to the id_token and to enrich the userinfo response.
tokenData
and idtokenData
can be assigned a name-value pair.
The advantage of this approach is the claim can be a complex object and no longer limited by STSUniversalUser string data type only.
Example usage:
var hok = {
"fingerprint#256": "aalweuaadg27ifafw8a2"
}
var grps = ["admin", "user"];
tokenData.cnf = hok;
tokenData.groups = grps;
3.1. Attribute mappings
In Definition, a user can define attribute mappings. Before the pre-token mapping rule is called, all attribute mappings are resolved, and the data (if any)
is available in the AttributeContainer
section of the STSUniversalUser
object.
3.2. Requested claims
To be able to do claim mapping, the user needs to know what claims are requested. According to OIDC specification, the claims can be requested by using scope values or requested by using claims parameter.
To make this process easier, the system considers both parameters and displays a claims
object of type RequestedClaims
that has the following interface:
Return Type | Method | Description | Arguments |
---|---|---|---|
string[] | getIDTokenEssentialClaims() | Return ID token essential claims. | |
string[] | getIDTokenVoluntaryClaims() | Return ID token voluntary claims. | |
string[] | getUserInfoEssentialClaims() | Return user information essential claims. | |
string[] | getUserInfoVoluntaryClaims() | Return user information voluntary claims. | |
string[] | getAllClaims() | Return all claims. | |
string[] | getIDTokenClaimValues(name) | Return values associated with an ID token claim. | string |
string[] | getUserInfoClaimValues(name) | Return values associated with a user information claim. | string |
For example, given scope=openid+email+phone
and claims
parameter as follows:
{
"userinfo": {
"given_name": { "essential":true },
"nickname": { "essential": false, "value": "Joe" },
"email": { "essential": true },
"http://example.info/claims/groups":null
},
"id_token": {
"given_name": { "essential": false },
"auth_time": { "essential": true },
"acr": { "values": [ "urn:mace:incommon:iap:gold", "urn:mace:incommon:iap:silver" ] }
}
}
Based on the previous example:
claims.getIDTokenEssentialClaims()
returns["auth_time"]
claims.getAllClaims()
returns["acr", "nickname", "email", "http://example.info/claims/groups", "phone_number", "phone_number_verified", "auth_time", "email_verified","given_name"]
.claims.getIDTokenClaimValues("acr")
returns["urn:mace:incommon:iap:gold", "urn:mace:incommon:iap:silver"]
claims.getUserInfoClaimValues("nickname")
returns["Joe"]
4. PostToken mapping rule
Post-token
mapping rule is the last chance to manipulate the response.
Similar to pre-token
mapping rule, in the JavaScript context, two JS map objects displayed: tokenData
and idtokenData
.
The tokenData
object contains the claim for introspect and the idtokenData
object contains the claim for id_token/userinfo
that were done in the pre-token
mapping rule.
User can manipulate extra parameters or extra headers in the response. For that purpose, two JS map objects are displayed: paramsOverride
and headersOverride
.
The paramsOverride
value can be of complex type because it can go to the query string, fragment, or response body.
The headersOverride
value must be kept as string because it goes to the response header.
Example usage:
headersOverride["x-fapi-corr-id"] = "gsdu2-22y2haw-23nafa-afw";
5. Extra mapping rules
Due to a different code structure, in IBM Security Verify Access OIDC Provider(ISVAOP) mapping rules are intended for specific purposes.
5.1. ROPC user authentication
In ISVA, the user authentication for ROPC is done in the pre-token
mapping rule. In the ISVAOP, the mapping rule ropc.js
performs the task.
The username
and password
that are received from the request are available in STSUniversalUser ContextAttribute
. When the user is authenticated, the claims that are related to the user are set into the JS map object userData
.
Example usage:
var username = stsuu.getContextAttributes().getAttributeValueByName("username");
var password = stsuu.getContextAttributes().getAttributeValueByName("password");
if (username == "peter" && password == "secret") { // Authenticate user via LDAP or HTTP call-out
userData.uid = "prabbit"; // This will be the `sub` of the token
userData.given_name = "peter";
userData.family_name = "rabbit";
userData.preferred_username = "[email protected]";
} else {
OAuthMappingExtUtils.throwSTSInvalidGrantMessageException("Invalid user or password.", "Either user or password is not valid.");
}
5.2. Dynamic client registration metadata customization
In ISVA, the pre-token
mapping rule is also used to manipulate the dynamic client registration payload for the following use cases:
- Setting the defaults for metadata that are not specified in the incoming payload. For example, if
grant_types
is not specified, the default isauthorization_code
. - Substitute metadata values that the OIDC provider does not support or wants to enforce. For example, the OIDC provider might want to enforce the
token_endpoint_auth_method
toprivate_key_jwt
only. - Further metadata validation. For example, in OpenBanking specification, some metadata needs to be bound by metadata inside the software statement. Other examples are to check software statement issuer, expiry, or other attributes.
- Throws an
invalid_client_metadata
error for metadata values that the OIDC provider does not like.
In ISVAOP, this goes to separate javascript file that can be configured in the provider.yml.
Definition object should be available under oauth_definition
context.
Each client metadata received is available as attributes under ContextAttribute
section.
Example usage:
// Setting default example
var grant_types = stsuu.getContextAttributes().getAttributeValuesByName("grant_types");
if (grant_types == null) {
stsuu.addContextAttribute(new Attribute("grant_types", "urn:ibm:names:ITFIM:oauth:body:param", "authorization_code"));
}
// Substitute metadata value example
var attr = stsuu.getContextAttributes().getAttributeByName("token_endpoint_auth_method");
if (attr != null) {
stsuu.getContextAttributes().removeAttribute(attr);
}
stsuu.addContextAttribute(new Attribute("token_endpoint_auth_method", "urn:ibm:names:ITFIM:oauth:body:param", "private_key_jwt"));
// Further metadata validation
var ss = stsuu.getContextAttributes().getAttributeValueByName("software_statement");
if (ss != null) { // Software statement exists
var iss = stsuu.getContextAttributes().getAttributeValueByName("iss");
if (iss != "http://openbankingdirectory.com") {
OAuthMappingExtUtils.throwSTSCustomUserMessageException("Unexpected software statement issuer.", 400, "invalid_client_metadata");
}
}
6. Differences between ISVAOP and ISVA-mapping rules
6.1. Differences in purposes
In ISVA, the pre-token
and post-token
mapping rules can be interpreted as user customization before and after the default implementation that is done in OAuth20TokenModule
. Therefore, the user authentication for the ROPC flow and the id_token claims that need to be resolved, must be done inside pre-token
mapping rule before the token can be generated. Because the state_id
, also called the grant_id
, is available only after token is generated, the association of extra data happens at post-token
. Also, post-token
is used to manipulate the final result, for example, in the introspection
and userinfo
endpoints. Although technically the manipulation can be done at pre-token
because the default implementation produces only a minimal response that unlikely needs to be overwritten.
In ISVAOP, as explained in PreToken Mapping Rule, PostToken Mapping Rule, and Additional Mapping Rules, each mapping rule can have different purpose. Therefore, some refactoring is needed.
- ROPC user authentication goes to a specific mapping rule.
- Introspect enrichment is done in
pre-token
instead ofpost-token
. Userinfo
enrichment is done together withid_token
in thepre-token
.- Dynamic Registration defaults, overrides, and further validation go to another mapping rule.
Also, things that currently are done in the mapping rules are now available OOTB or through configuration. For examples:
- The enforcement that authorization code can be used only once.
- Filtering of scopes
- Enforcement of
jti
uniqueness - Validation of various JWTs
6.2. Differences in objects
Some of the differences are:
- Definition object is displayed by default as
oauth_definition
. - Java
List
andMap
are replaced with a JS list and object, so some interfaces that are available as part of that Java might no longer be there. - The introduction of new objects such as
claims
,userData
,tokenData
,paramsOverride
.
Updated 3 months ago