Identity Cloud

Scripted decision node API

A Scripted Decision node calls server-side JavaScript to set the outcome for the node programmatically and determine the path the authentication journey takes. The script can perform actions before setting the outcome.

To create a decision script, browse to Scripts > Auth Scripts in the Identity Cloud admin UI, click + New Script, and select Journey Decision Node.

A script can set the outcome directly, or use the Action interface:

outcome

Assign string values to the outcome variable.

if (expectedCondition) {
  outcome = "true"
} else {
  outcome = "false"
}

When configuring the Scripted Decision node, add the two outcomes true and false in Outcomes. Each outcome appears as a node endpoint that you connect to other nodes in the journey.

The outcome strings are not limited to true and false. Specify a value for each possible outcome and as many outcomes as required by the journey.

Action

Use the Action interface to define the outcome or to perform an operation.

var fr = JavaImporter(
    org.forgerock.openam.auth.node.api.Action
)

if (expectedCondition) {
  // Set outcome to "true", and set a custom session property:
  action = fr.Action.goTo("true").putSessionProperty("customKey", "customValue").build()
} else {
  // Set outcome to "false", and add an "errorMessage" in the shared node state:
  action = fr.Action.goTo("false").withErrorMessage("Friendly error description.").build()
}

An outcome specified with the Action.goTo method overrides the value set for the outcome variable:

action = Action.goTo("false").build() // Evaluation continues along the "false" outcome.
outcome = "true"                      // No effect.

A script can also use the Action interface to set session properties and callbacks. For details about the Action interface, refer to the Javadoc for Action.

Scripted decision node bindings

A script has access to Identity Cloud bindings, objects that Identity Cloud injects into the script execution context:

Binding Information

auditEntryDetail

Add information to the Identity Cloud audit logs.

Refer to Audit information.

callbacks

Request additional data from the user using a callback.

Refer to Callbacks.

existingSession

If the user has previously authenticated and has a session, use this variable to access the properties of that session.

This binding is only present when performing a session upgrade. Any properties added by nodes earlier in the journey only appear on the user’s new session when the journey completes; such properties are not available in the existingSession binding.

httpClient

Make outbound HTTP calls.

For details, refer to the Javadoc for ChfHttpClient.

idRepository

Access the data stored in the user’s profile.

logger

Write a message to the Identity Cloud debug log.

In Identity Cloud, this corresponds to the am-core log source.

The name of the debug logger takes the form scripts.AUTHENTICATION_TREE_DECISION_NODE.script-id.

For details, refer to the Javadoc for Debug.

realm

Return the name of the realm to which the user is authenticating as a string.

For example, authenticating to the alpha realm returns a string value of /alpha.

requestHeaders

Access the HTTP headers provided in the login request.

requestParameters

Access the HTTP request parameters provided in the login request.

resumedFromSuspend

Boolean—​True if evaluation has resumed after suspension; for example, after sending an email and receiving the response.

secrets

Access the secrets configured for Identity Cloud in the environment.

For details, refer to the Javadoc for ScriptedSecrets.

nodeState

Access data set by previous nodes in the journey, or store data to be used by subsequent nodes.

Access request header data

A script can access the headers in the login request with the methods of the requestHeaders object.

The script has access to a copy of the headers. Changing their values does not affect the request.

Methods

String[] requestHeaders.get(String headerName)

Returns an array of the values in the named request header, or null, if the property is not set.

Header names are case-sensitive.

Example
if (requestHeaders.get("user-agent").get(0).indexOf("Chrome") !== -1) {
    outcome = "true"
} else {
    outcome = "false"
}

Access request parameter data

A script can access the query parameters in the login request with the methods of the requestParameters object.

The script has access to a copy of the parameters. Changing their values does not affect the request.

Methods

String[] requestParameters.get(String parameterName)

Return an array of the values in the named request parameter, or null, if the parameter is not available.

Example
var service
var authIndexType = requestParameters.get("authIndexType")
if (authIndexType && String(authIndexType.get(0)) === "service") {
    service = requestParameters.get("authIndexValue").get(0)
}

if (service) {
    nodeState.putShared("service", service)
}

outcome = "true"

In JavaScript, the requestParameters values have a typeof of object.

Convert the values to strings before using strict equality comparisons.

Access shared state data

A script can access the shared state of the journey with the methods of the nodeState object. There are three types of state:

Shared

Non-sensitive state.

Transient

Sensitive state.

Transient state data is never sent back to the user’s browser in a callback so does not need to be encrypted.

Secure

Encrypted sensitive state.

Secure state data is sent back to the user’s browser encrypted as part of the shared state object.

Transient state data is promoted to secure state data when:

  • A callback to the user is about to occur.

  • A downstream node is detected in the journey, requesting data in the transient state as script input.

Unless the downstream node explicitly requests the secure state data by name, the authentication journey removes it from the node state after processing the next callback.

For example, a node in a registration journey stores a user’s password in transient state. The node sends a callback to the user before an inner tree node, downstream in the journey, consumes that password. As part of the callback, the journey assesses what to add to the secure state. It does this by checking the state inputs that downstream nodes in the journey require. Nodes that only request * are ignored, as this would result in putting everything that’s in transient state into secure state, and retaining sensitive information longer than necessary.

If a downstream node requires the password, it must therefore explicitly request it as state input, even if it lists the * wildcard as input.

Identity Cloud stores shared state properties as name-value pairs. Property names are case-sensitive.

Methods

For additional methods and details, refer to the Javadoc for NodeState.

JsonValue nodeState.get(String propertyName)

Returns the value of the named property.

The value may come from the transient, secure, or shared states, in that order. If the same property is available in several states, the method returns the value of the property in the transient state first.

If the property is not set, the method returns null.

Example
var currentAuthLevel = nodeState.get("authLevel")
var thePassword = nodeState.get("password")

outcome = "true"
NodeState nodeState.putShared(String propertyName, String propertyValue)

Sets the value of the named shared state property.

Example
try {
  var thePassword = nodeState.get("password").asString()
} catch (e) {
  nodeState.putShared("errorMessage", e.toString())
}

outcome = "true"
NodeState nodeState.putTransient(String propertyName, String propertyValue)

Sets the value of the named transient state property.

Example
nodeState.putTransient("sensitiveKey", "sensitiveValue")

outcome = "true"

Access profile data

A script can access profile data through the methods of the idRepository object.

Methods

Set idRepository.getAttribute(String userName, String attributeName)

Returns the values of the named attribute for the named user.

void idRepository.setAttribute(String userName, String attributeName, Array attributeValues)

Sets the named attribute as specified by the attribute value for the named user, and persists the result in the user’s profile.

In this example, an Identity Store Decision node with Username as Universal Identifier enabled precedes the Scripted Decision node that uses the script:

var username = nodeState.get("username").asString()
idRepository.setAttribute(username, "mail", ["test@example.com"])

outcome = "true"
void idRepository.addAttribute(String userName, String attributeName, String attributeValue)

Add an attribute value to the list of attribute values associated with the attribute name for a particular user.

Example
var username = nodeState.get("username").asString()
var attribute = "mail"

// Add a value as a string.
idRepository.addAttribute(username, attribute, "user@example.com")

// idRepository.getAttribute(username, attribute).toString() > [test@example.com, user@example.com]

// Get the first value:
// idRepository.getAttribute(username, attribute).iterator().next()) > test@example.com

// Get a value at the specified index:
// idRepository.getAttribute(username, attribute).toArray()[1] > user@example.com

// If no attribute by this name is found, the result is an empty array:
// idRepository.getAttribute(username, "non-existent-attribute").toString() > []

outcome = "true"

Set session properties

A script can create session properties with the fields and methods of the Action interface.

The following example sets the outcome to true, and adds a custom session property:

var goTo = org.forgerock.openam.auth.node.api.Action.goTo
action = goTo("true").putSessionProperty("mySessionProperty", "myPropertyValue").build()

When adding a session property, make sure it is in the Identity Cloud allow list. Identity Cloud does not allow the script to add it to sessions unless it is in the allow list.

  1. In AM admin UI, under Realms > Realm Name > Services, make sure the Session Property Whitelist Service is configured.

    If not, configure the service with the default settings.

  2. Add the property name to the Allowlisted Session Property Names list unless it is already present.

For details, refer to Session Property Whitelist Service.

Add the script to a Scripted Decision node in your authentication journey. When a user authenticates successfully, Identity Cloud adds the property to their session, as shown in the following output for session introspection:

{
  "username": "014c54bd-6078-4639-8316-8ce0e7746fa4",
  "universalId": "id=014c54bd-6078-4639-8316-8ce0e7746fa4,ou=user,o=alpha,ou=services,ou=am-config",
  "realm": "/alpha",
  "latestAccessTime": "2022-10-31T11:03:25Z",
  "maxIdleExpirationTime": "2022-10-31T11:33:25Z",
  "maxSessionExpirationTime": "2022-10-31T13:03:24Z",
  "properties": {
    "AMCtxId": "de5abe95-db97-4354-9d32-aab660ea23a3-4252446",
    "mySessionProperty": "myPropertyValue"
  }
}

Access existing session properties

A script can access existing session properties during a session upgrade request with the existingSession.get method.

Methods

String existingSession.get(String propertyName)

Returns the string value of the named existing session property, or null, if the property is not set.

If the current request is not a session upgrade and does not provide an existing session, the existingSession variable is not declared. Check for a declaration before attempting to access the variable.

Example
if (typeof existingSession !== 'undefined') {
    var existingAuthLevel = existingSession.get("AuthLevel")
} else {
    nodeState.putShared("errorMessage", "Variable existingSession not declared - not a session upgrade.")
}

outcome = "true"

Callbacks

A script can use callbacks to provide or prompt for additional information during the authentication process.

The following script uses the NameCallback callback to request a "Nickname" value from the user. It adds the nickname to the nodeState object for use by subsequent nodes in the authentication journey:

var fr = JavaImporter(
  org.forgerock.openam.auth.node.api,
  javax.security.auth.callback.NameCallback
)

if (callbacks.isEmpty()) {
  action = fr.Action.send(new fr.NameCallback("Enter Your Nickname")).build()
} else {
  nodeState.putShared("Nickname", callbacks.get(0).getName())
  action = fr.Action.goTo("true").build()
}

outcome = "true";

For a list of supported callbacks, refer to Supported callbacks.

Audit information

A script can add information to audit log entries with the auditEntryDetail variable. The auditEntryDetail variable can hold either a string or a JSON object.

Identity Cloud appends the value of the variable to the authentication audit logs, which corresponds to the am-authentication log source.

The following JavaScript adds a string to the authentication audit log entry:

auditEntryDetail = "Additional audit information"

outcome = "true"

Identity Cloud records it in the payload > entries > info > nodeExtraLogging > auditInfo field of the audit log entry:

{
  "payload": {
    "_id": "de5abe95-db97-4354-9d32-aab660ea23a3-4392064",
    "component": "Authentication",
    "entries": [{
      "info": {
        "authLevel": "0",
        "displayName": "Scripted Decision",
        "nodeExtraLogging": {
          "auditInfo": "Additional audit information"
        },
        "nodeId": "d3a9a765-f5d7-41dd-b936-f862c8b672a2",
        "nodeOutcome": "true",
        "nodeType": "ScriptedDecisionNode",
        "treeName": "Test"
      }
    }],
    "eventName": "AM-NODE-LOGIN-COMPLETED",
    "level": "INFO",
    "principal": [
      "014c54bd-6078-4639-8316-8ce0e7746fa4"
    ],
    "realm": "/alpha",
    "source": "audit",
    "timestamp": "2022-10-31T13:06:38.195Z",
    "topic": "authentication",
    "trackingIds": [
      "de5abe95-db97-4354-9d32-aab660ea23a3-4391910"
    ],
    "transactionId": "bd52ff36-8b34-4819-8e50-68ca1961275b-request-4/0"
  },
  "timestamp": "2022-10-31T13:06:38.195401296Z",
  "type": "application/json",
  "source": "am-authentication"
}
Copyright © 2010-2023 ForgeRock, all rights reserved.