Get an access token in a journey
While this PingOne Advanced Identity Cloud use case was validated for accuracy, it can always be improved. To provide feedback, click or in the top right of this page (you must be logged into Backstage). |
Description
Estimated time to complete: 25 minutes
In this use case, create a script to get an access token using a service account. A service account lets you request access tokens for most Advanced Identity Cloud REST API endpoints. Then, create a simple journey with this script to prove you can successfully request an access token.
The script in this use case uses the Service accounts can only be used with Advanced Identity Cloud API endpoints; if you want to communicate with a third-party API, you’ll need to use the standard OAuth 2.0 client credential flow. |
Goals
After completing this use case, you will know how to do the following:
-
Get an access token to use in API calls in a Scripted Decision node in Advanced Identity Cloud.
Prerequisites
Before you start work on this use case, ensure you have these prerequisites:
-
A basic understanding of:
-
Journeys and nodes
-
JavaScript
-
Service accounts
-
ESVs
-
-
Access to your Advanced Identity Cloud development environment as a tenant administrator.
-
A service account that can grant the
fr:idm:*
scope to an access token.You’ll need the service account ID and private key later when you create ESVs.
Tasks
Task 1: Create ESVs
-
In the Advanced Identity Cloud admin UI, go to Tenant Settings > Global Settings > Environment Secrets & Variables.
-
Create the following ESVs:
ESV name ESV type Description esv-tenant-env-fqdn
Variable (string)
Tenant FQDN (in the appropriate FQDN format); for example:
openam-mycompany-ew2.id.forgerock.io
esv-service-account-id
Secret
Service account ID; for example:
449d7e27-7889-47af-a736-83b6bbf97ec5
esv-service-account-privatekey
Secret
Service account private key; for example:
{ d: "RhpIZ32rNfkoVkQh3pt1me...rDkFL9nBWDOZvXQ2LsFEBc", dp: "RfrvtBH_NmzxS......IpJ1vYZS_J0cw", dq: "OVO8_yXFRHT...2VREB2b8f8xvIhv5jrQWQ", e: "AQAB", kty: "RSA", n: "5LoH3Fc8IdRg1...K4eUvMEJsjVvfRgzpWCDM0", p: "_wjzIYyYcQiNOZdV1Cp7...kjDw", q: "5ZeYq0C......6WOaiYw", qi: "Z9ECeon...q56tpl2Mu65yqlw", }
-
Apply the updates.
Learn more about creating ESVs and applying updates in Manage ESVs using the UI .
Task 2: Create a script to get an access token
-
Download the sample script: getAccessToken.js.
View script
/* * Copyright © 2024 Ping Identity Corporation * * This code is to be used exclusively in connection with Ping Identity Corporation * software or services. * Ping Identity Corporation only offers such software or services to legal entities * who have entered into a binding license agreement with Ping Identity Corporation. */ var nodeConfig = { nodeName: "Get Access Token Demo", tenantFqdnEsv: "esv.tenant.env.fqdn", accountIdEsv: "esv.service.account.id", privateKeyEsv: "esv.service.account.privatekey", accessTokenStateField: "idmAccessToken", maxAttempts: 3, scope: "fr:idm:*", serviceAccountClientId: "service-account", jwtValiditySeconds: 10, }; var nodeLogger = { debug: function (message) { logger.message("***" + nodeConfig.nodeName + " " + message); }, warning: function (message) { logger.warning("***" + nodeConfig.nodeName + " " + message); }, error: function (message) { logger.error("***" + nodeConfig.nodeName + " " + message); }, }; var nodeOutcomes = { SUCCESS: "Success", ERROR: "Error", }; var javaImports = JavaImporter( org.forgerock.openam.auth.node.api.Action, org.forgerock.json.jose.builders.JwtBuilderFactory, org.forgerock.json.jose.jwt.JwtClaimsSet, org.forgerock.json.jose.jws.JwsAlgorithm, org.forgerock.json.jose.jws.SignedJwt, org.forgerock.json.jose.jws.handlers.SecretRSASigningHandler, org.forgerock.json.jose.jwk.RsaJWK, javax.crypto.spec.SecretKeySpec, org.forgerock.secrets.SecretBuilder, org.forgerock.secrets.keys.SigningKey, java.time.temporal.ChronoUnit, java.time.Clock, java.util.UUID ); function getKeyFromJwk(issuer, jwk) { var privateKey = javaImports.RsaJWK.parse(jwk).toRSAPrivateKey(); var secretBuilder = new javaImports.SecretBuilder(); secretBuilder .secretKey(privateKey) .stableId(issuer) .expiresIn( 5, javaImports.ChronoUnit.MINUTES, javaImports.Clock.systemUTC() ); return new javaImports.SigningKey(secretBuilder); } function getAssertionJwt(accountId, privateKey, audience, validity) { var signingHandler = new javaImports.SecretRSASigningHandler( getKeyFromJwk(accountId, privateKey) ); var iat = new Date().getTime(); var exp = new Date(iat + validity * 1000); var jwtClaims = new javaImports.JwtClaimsSet(); jwtClaims.setIssuer(accountId); jwtClaims.setSubject(accountId); jwtClaims.addAudience(audience); jwtClaims.setExpirationTime(exp); jwtClaims.setJwtId(javaImports.UUID.randomUUID()); var jwt = new javaImports.JwtBuilderFactory() .jws(signingHandler) .headers() .alg(javaImports.JwsAlgorithm.RS256) .done() .claims(jwtClaims) .build(); return jwt; } function getAccessToken(accountId, privateKey, tenantFqdn, maxAttempts) { var response = null; var accessToken = null; var tokenEndpoint = "https://" .concat(tenantFqdn) .concat("/am/oauth2/access_token"); nodeLogger.debug("Getting Access Token from endpoint " + tokenEndpoint); var assertionJwt = getAssertionJwt( accountId, privateKey, tokenEndpoint, nodeConfig.jwtValiditySeconds ); if (!assertionJwt) { nodeLogger.error("Error getting assertion JWT"); return null; } nodeLogger.debug("Got assertion JWT " + assertionJwt); for (var attempt = 0; attempt < maxAttempts; attempt++) { nodeLogger.debug("Attempt " + (attempt + 1) + " of " + maxAttempts); try { var request = new org.forgerock.http.protocol.Request(); request.setUri(tokenEndpoint); request.setMethod("POST"); request .getHeaders() .add("Content-Type", "application/x-www-form-urlencoded"); var params = "grant_type=" .concat( encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer") ) .concat("&client_id=") .concat(encodeURIComponent(nodeConfig.serviceAccountClientId)) .concat("&assertion=") .concat(encodeURIComponent(assertionJwt)) .concat("&scope=") .concat(encodeURIComponent(nodeConfig.scope)); request.setEntity(params); response = httpClient.send(request).get(); if (response) { break; } } catch (e) { nodeLogger.error( "Failure calling access token endpoint: " + tokenEndpoint + " exception:" + e ); } } if (!response) { nodeLogger.error("Bad response"); return null; } if (response.getStatus().getCode() !== 200) { nodeLogger.error( "Unable to acquire Access Token. HTTP Result: " + response.getStatus() ); return null; } try { var responseJson = response.getEntity().getString(); nodeLogger.debug("Response content " + responseJson); var oauth2response = JSON.parse(responseJson); accessToken = oauth2response.access_token; nodeLogger.debug("Access Token acquired: " + accessToken); return accessToken; } catch (e) { nodeLogger.error("Error getting access token from response: " + e); } return null; } (function () { try { nodeLogger.debug("Node starting"); var accessToken = nodeState.get(nodeConfig.accessTokenStateField); if (accessToken) { nodeLogger.debug("Access token already present: continuing"); action = javaImports.Action.goTo(nodeOutcomes.SUCCESS).build(); return; } var tenantFqdn = systemEnv.getProperty(nodeConfig.tenantFqdnEsv); if (!tenantFqdn) { nodeLogger.error("Couldn't get FQDN from esv " + config.tenantFqdnEsv); action = javaImports.Action.goTo(nodeOutcomes.ERROR).build(); return; } var accountId = systemEnv.getProperty(nodeConfig.accountIdEsv); if (!accountId) { nodeLogger.error( "Couldn't get service account id from esv " + nodeConfig.accountIdEsv ); action = javaImports.Action.goTo(nodeOutcomes.ERROR).build(); return; } var privateKey = systemEnv.getProperty(nodeConfig.privateKeyEsv); if (!privateKey) { nodeLogger.error( "Couldn't get private key from esv " + nodeConfig.privateKey ); action = javaImports.Action.goTo(nodeOutcomes.ERROR).build(); return; } accessToken = getAccessToken( accountId, privateKey, tenantFqdn, nodeConfig.maxAttempts ); if (!accessToken) { nodeLogger.error("Failed to get access token"); action = javaImports.Action.goTo(nodeOutcomes.ERROR).build(); return; } nodeLogger.debug("Success - adding token to transient state"); nodeState.putTransient(nodeConfig.accessTokenStateField, accessToken); action = javaImports.Action.goTo(nodeOutcomes.SUCCESS).build(); } catch (e) { nodeLogger.error("Exception encountered " + e); action = javaImports.Action.goTo(nodeOutcomes.ERROR).build(); return; } })();
-
In the Advanced Identity Cloud admin UI, go to Scripts > Auth Scripts and click + New Script.
-
Select Journey Decision Node and click Next.
-
Select Legacy and click Next.
-
In the New Journey Decision Node Script window, enter the following details:
Field Value Name
Get access token
Description
Get an access token using a service account
JavaScript
Replace the sample JavaScript with the contents of the downloaded
getAccessToken.js
script. -
Check the variables defined in the script and update as needed:
var nodeConfig = { nodeName: "Get Access Token Demo", (1) tenantFqdnEsv: "esv.tenant.env.fqdn", (2) accountIdEsv: "esv.service.account.id", (3) privateKeyEsv: "esv.service.account.privatekey", (4) accessTokenStateField: "idmAccessToken", maxAttempts: 3, scope: "fr:idm:*", (5) serviceAccountClientId: "service-account", (6) jwtValiditySeconds: 10, };
1 The nodeName
indicates the name of your journey for debugging purposes.2 The tenantFqdnEsv
contains the script reference to theesv-tenant-env-fqdn
ESV.3 The accountIdEsv
contains the script reference to theesv-service-account-id
ESV.4 The privateKeyEsv
contains the script reference to theesv-service-account-privatekey
ESV.5 The scope
you chose when you set up your service account; this determines which API endpoints you can get an access token for.6 The serviceAccountClientId
must be set toservice-account
to use the built-in OAuth 2.0 public client for service accounts; otherwise, the JWT profile for OAuth 2.0 authorization grant flow will fail. -
Click Save and Close.
Task 3: Create a journey to get an access token
-
In the Advanced Identity Cloud admin UI, go to Journeys and click + New Journey.
-
In the New Journey window, enter the following details:
Field Value Name
Get Access Token Demo
Identity Object
Alpha realm - Users
managed/alpha_user
Description
Journey to get an access token using a service account
-
Click Save. The journey editor displays.
-
In the journey editor, search for the
Scripted Decision
node and drag it onto the canvas. -
Configure this node as follows:
Field Value Name
Get Access Token
Script
Get access token
Outcomes
Create two outcomes:
Success
andError
-
In the journey editor, search for the
Message Node
and drag two copies of it onto the canvas. -
Select the first
Message Node
and configure it as follows:Field Value Name
Success Message
Message
Key
en
Value
Access token successfully acquired
-
Select the second
Message Node
and configure it as follows:Field Value Name
Error Message
Message
Key
en
Value
Failed to get access token
-
Connect the nodes:
Source node Outcome path Target node Start (person icon)
→
Scripted Decision node
(
Get Access Token
)Scripted Decision node
(
Get Access Token
)Success
Message node
(
Success Message
)Error
Message node
(
Error Message
)Message node
(
Success Message
)True
Success node
False
Success node
Message node
(
Error Message
)True
Failure node
False
Failure node
-
Click Save.
Validation
Now that you have created a journey to get an access token, you are ready to validate it.
The journey runs the script to acquire an access token using the service account and ESVs you set up. If an access token is successfully acquired, the Success Message is shown.
If you want to view the access token created during testing, you can enable debug mode in your development environment. |
Steps
-
In the Advanced Identity Cloud admin UI, go to Journeys and click on the
Get Access Token Demo
journey you just created. -
In the Preview URL field, click and paste the URL into an Incognito window.
The script runs to get an access token, and if successful, the Success Message displays:
The Yes and No buttons shown are the default outcomes for a Message node; they are not relevant to this example and don’t do anything further.
If an access token is not acquired, the Error Message is shown instead (
Failed to get access token
).
Explore further
Reference material
Reference | Description |
---|---|
Information about service accounts in Advanced Identity Cloud. |
|
Authenticate to Advanced Identity Cloud REST API with access token |
Learn how to authenticate to Advanced Identity Cloud REST API endpoints using an access token. |
Information about environment secrets and variables (ESVs). |
|
Conceptual information on journeys and their purpose in Advanced Identity Cloud. |
|
Learn about the configurable nodes Advanced Identity Cloud offers for use in journeys. |
|
Reference information for journey decision node scripts. |