How do I make session properties from a journey available in the OIDC ID token in Identity Cloud?
The purpose of this article is to provide assistance with making session properties from a journey available in the OpenID Connect (OIDC) ID token. This can be achieved by adding custom claims to the OIDC claims script in ForgeRock Identity Cloud. For example, this allows you to capture specific information when a user logs in, store that in a journey session property, retrieve it in the OIDC claims script and then add it to a custom claim within the JWT ID token for use elsewhere.
3 readers recommend this article
Overview
The process outlined in this article uses a login journey to capture details when a user authenticates and then stores those details in a journey session property. By adding a custom claim to the OIDC claims script, you can then retrieve the stored session property and add it to the ID token as a custom claim.
Steps involved
- Create login journey
- Allowlist the session property
- Create a custom OIDC Claims Script
- Configure the OAuth2 provider
- Update the OAuth 2.0/OIDC client
- Validate the JWT ID token contains the journey session properties
The examples in this article use a session property called mySessionProperty and an additional scope called myCustomScope (which adds the session property to the id_token).
Note
Writing scripts for end user journeys and OIDC claims is outside the scope of ForgeRock support; if you want more tailored advice, consider engaging Deployment Support Services.
Integration with IG
If you use IG to consume the resulting ID token, you may find the session property is null. This can happen in the following scenario: the ID token is issued successfully with the custom claim containing your session property. Subsequently, IG makes a call to /userinfo, which causes the OIDC claims script to be invoked again, but this time there is no session, which leads to a null session property.
If you experience this issue, you should modify the OIDC claims script to ensure it only adds claims when a session exists.
Prerequisites
- You have a working Identity Cloud tenant.
- You have an existing OAuth 2.0/OIDC client in Identity Cloud for use with the OIDC Provider. See Applications for further information.
Creating a login journey
Creating a login journey can be quite straightforward or may need scripting knowledge depending on what you want to set in your journey session property:
- If you just want to update a session property with static values, you can use the Set Session Properties node. See Set Session Properties node for further information. In the following example journey, the Set Session Properties node would replace the two Scripted Decision nodes to simplify your journey.
- If you want to retrieve a value using some logic and store that value as a session property, you will need to use Scripted Decision nodes and write suitable scripts to achieve it.
To create a login journey:
- In the Identity Cloud admin UI, go to Journeys and click New Journey.
- Enter a name for your journey, select which identities will authenticate using this journey, and click Save.
- Create a login journey, which authenticates the user, retrieves the required piece of information, and then updates a session property. For example, your journey may look similar to this, where the Retrieve Information and Update Session Property nodes are Scripted Decision nodes:
- Click Save.
See Set session properties and Access existing session properties for further information and examples on using Scripted Decision Node scripts to set and access session properties using the Action
interface and the existingSession
object respectively.
There are example scripts in GitHub that demonstrate retrieving a user's location from their IP address and setting that as a session property. You can refer to these scripts as a starting point for your own scripts (but they are not supported):
Allowlisting the session property
All session properties are considered sensitive and are invisible to everyone, including admins, by default. In order to make them accessible, you must allowlist them.
- In the Identity Cloud admin UI, go to Native Consoles > Access Management > Services > Session Property Whitelist Service.
- Add the name of your session property to both fields. For example, it would look similar to this after adding a property called mySessionProperty:
- Click Save Changes.
Creating a custom OIDC Claims Script
- In the Identity Cloud admin UI, go to Scripts > Auth Scripts > OIDC Claims Script.
- Click the … menu and select Duplicate.
- Enter a unique name for your script and optionally a description.
- Specify the script details:
- Add the session property claim to a new scope in the
utils.setScopeClaimsMap
section. For example, with a new myCustomScope scope: utils.setScopeClaimsMap({ profile: [ 'name', 'family_name', 'given_name', 'zoneinfo', 'locale' ], email: ['email'], address: ['address'], phone: ['phone_number'], myCustomScope: ['mySessionProperty'] }); - Add mapping details for the session property claim to the
utils.setClaimResolvers
section. For example, with added mapping details for the mySessionProperty claim: utils.setClaimResolvers({ /* // An example of a simple claim resolver function that is defined for a claim // directly in the configuration object: custom-claim-name: function (requestedClaim) { // In this case, initially, the claim value comes straight from a user profile attribute value: var claimValue = identity.getAttribute('custom-attribute-name').toArray()[0] // Optionally, provide additional logic for processing (filtering, formatting, etc.) the claim value. // You can use: // requestedClaim.getName() // requestedClaim.getValues() // requestedClaim.getLocale() // requestedClaim.isEssential() return claimValue }, */ /** * The use of utils.getUserProfileClaimResolver shows how * an argument passed to a function that returns a claim resolver * becomes available to the resolver function (via its lexical context). */ name: utils.getUserProfileClaimResolver('cn'), family_name: utils.getUserProfileClaimResolver('sn'), given_name: utils.getUserProfileClaimResolver('givenname'), zoneinfo: utils.getUserProfileClaimResolver('preferredtimezone'), locale: utils.getUserProfileClaimResolver('preferredlocale'), email: utils.getUserProfileClaimResolver('mail'), address: utils.getAddressClaimResolver( /** * The passed in user profile claim resolver function * can be used by the address claim resolver function * to obtain the claim value to be formatted as per the OIDC specification: * @see https://openid.net/specs/openid-connect-core-1_0.html#AddressClaim. */ utils.getUserProfileClaimResolver('postaladdress') ), phone_number: utils.getUserProfileClaimResolver('telephonenumber'), mySessionProperty: function () { return session.getProperty('mySessionProperty'); } }); - Update any other script details as needed.
- Add the session property claim to a new scope in the
- Click Save.
Configuring the OAuth2 provider
- In the Identity Cloud admin UI, go to Native Consoles > Access Management > Services > OAuth2 Provider > Advanced OpenID Connect and enable Always Return Claims in ID Tokens.
- Click Save Changes.
Updating the OAuth 2.0/OIDC client
- In the Identity Cloud admin UI, go to Native Consoles > Access Management > Applications > OAuth 2.0 > Clients and click the name of your OAuth 2.0/OIDC client.
- Select the OAuth2 Provider Overrides tab and select the custom OIDC claims script you created above in the OIDC Claims Script field.
- Ensure the Enable OAuth2 Provider Overrides option is selected.
- Click Save Changes.
- In the Identity Cloud admin UI, go to Applications and click the name of your OAuth 2.0/OIDC client.
- Add the new scope you specified in the script above in the Scopes field.
- Click Save.
Validating the JWT ID token contains the journey session properties
- Authenticate using the login journey you created and obtain an authorization code using the resulting journey session token:
- Navigate to a URL such as the following in a browser using Incognito or Browsing mode; ensure you include the scope you specified in the script above (myCustomScope in this example):https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/authorize?client_id=<client_id>&response_type=code&scope=openid%20myCustomScope&redirect_uri=https://httpbin.org/anything&service=<login_journey_name>
- Authenticate as an end user.
- Allow access to your account when prompted for consent.
- Copy the authorization code returned in the browser, for example:{ "args": { "client_id": "<client_id>", "code": "8xjrUVHHR5i5t_Fkpp3UUr6NBJ8.spkaJhs1d63p7qILFOVrHGaAlp8", ...
- Exchange the authorization code for the id_token:$ curl --location --request POST 'https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/access_token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'code=<authorization-code>' \ --data-urlencode 'client_id=<client_id>' \ --data-urlencode 'client_secret=<client_secret>' \ --data-urlencode 'redirect_uri=https://httpbin.org/anything'
See Identity Cloud Postman collection for further information.
- Copy the id_token returned (do not copy the entire response as that also includes the access_token).
- Decode the id_token to verify that the JWT includes the session property. For example, you can use jq on the command line (you can install jq as outlined in Download jq):$ jq -R 'split(".") | .[1] | @base64d | fromjson' <<< <id_token>Example response showing the session property (mySessionProperty in this example):{ "at_hash": "PtgPFhutEQ4eHK1_nEVmPQ", "sub": "bddb135d-f6b7-4933-bb9e-525d436d48bb", "auditTrackingId": "6a8d9c63-3154-4094-8713-63e19368d518-27339", "subname": "bddb135d-f6b7-4933-bb9e-525d436d48bb", "iss": "https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha", "tokenName": "id_token", "sid": "s+g7AVR2lNE6C9t3jx+Tn9VBPO7yVn2xMLHrpH2NAjA=", "aud": "<client_name>", "c_hash": "t8R_lQDDmeQRQe3Pbfn6rg", "acr": "0", "org.forgerock.openidconnect.ops": "5-kDQ_4m8XueDlHI0x6mYuKG9To", "azp": "<client_name>", "auth_time": 1637582507, "mySessionProperty": "customValue", "realm": "/alpha", "exp": 1637586136, "tokenType": "JWTToken", "iat": 1637582536 }