---
title: Scripted decision node API
description: The scripted decision node lets you write a server-side script, in JavaScript or Groovy, to determine the path the authentication journey takes.
component: pingam
version: 7.2
page_id: pingam:authentication-guide:scripting-api-node
canonical_url: https://docs.pingidentity.com/pingam/8.1/am-scripting/scripting-api-node.html
llms_txt: https://docs.pingidentity.com/pingam/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
superseded_by: https://docs.pingidentity.com/pingam/8.1/am-scripting/scripting-api-node.html
section_ids:
  scripting-api-node-requestHeaders: Accessing request header data
  methods: Methods
  scripting-api-node-requestParameters: Accessing request parameter data
  methods_2: Methods
  scripting-api-node-nodeState: Accessing shared state data
  methods_3: Methods
  scripting-api-node-id-repo: Accessing profile data
  methods_4: Methods
  scripting-api-node-sessionProperties: Setting session properties
  scripting-api-node-existingSession: Accessing existing session properties
  methods_5: Methods
  scripting-api-node-callbacks: Using Callbacks
  scripting-api-authn-secrets: Accessing credentials and secrets
  methods_6: Methods
  scripting-api-node-audit-logging: Adding audit information
---

# Scripted decision node API

The scripted decision node lets you write a server-side script, in JavaScript or Groovy, to determine the path the authentication journey takes.

These scripts have access to a number of *bindings*, which provide the context to help you make the decision.

The primary role of a scripted decision node is to specify the possible paths a user can take. There are two methods to define these paths within the script:

* `outcome`

  The simplest method is to assign one or more string values to the `outcome` variable.

  ```javascript
  if (...) {
    outcome = "true"
  } else {
    outcome = "false"
  }
  ```

  When configuring the scripted decision node in an authentication tree, add the two outcomes `true` and `false`, and connect them to other parts of the tree, so that tree evaluation can continue.

  You can specify as many outcomes as required in your scripts; for example, you might have `hours`, `days`, and `months`. Be sure to specify each possible outcome when designing your authentication journey.

* `Action`

  You can use the `Action` interface to define the script outcome and/or specify an operation to perform, for example, by chaining with `ActionBuilder` functions.

  > **Collapse: functions:**
  >
  > | Method                                                                                                                                | Information                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
  > | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  > | `public ActionBuilder putSessionProperty(String key, String value)`                                                                   | Add a session property.Refer to [Setting session properties](#scripting-api-node-sessionProperties).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
  > | `public ActionBuilder removeSessionProperty(String key)`                                                                              | Remove an existing session property.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
  > | `public ActionBuilder withDescription(String description)`                                                                            | Set a description for the action.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
  > | `public ActionBuilder withErrorMessage(String message)`                                                                               | Set an error message to display to the end user when the journey reaches the Failure node.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
  > | `public ActionBuilder withIdentifiedIdentity(AMIdentity id)``public ActionBuilder withIdentifiedIdentity(String username, IdType id)` | Set the identity, authenticated or not, of the user or agent verified to exist in an identity store.Use these methods to record the type of identified user. If the advanced server property, `org.forgerock.am.auth.trees.authenticate.identified.identity` is set to true, AM uses the stored identified identities to decide who to log in.This lets the authentication tree engine correctly resolve identities that have the same username.For more information, refer to [advanced server properties](../reference/deployment-configuration-reference.html#org.forgerock.am.auth.trees.authenticate.identified.identity). |
  > | `public ActionBuilder withLockoutMessage(String message)`                                                                             | Set an error message to display to the end user when the account is locked or inactive.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
  > | `public ActionBuilder withStage(String stage)`                                                                                        | Set a stage name to return to the client to aid the rendering of the UI. The property is only sent if the script also sends callbacks.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
  >
  > For the full API, refer to [ActionBuilder](../_attachments/apidocs/org/forgerock/openam/auth/node/api/Action.ActionBuilder.html).

  Example:

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

  if (...) {
    // Set outcome to "true", and create and populate a custom session property:
    action = fr.Action.goTo("true").putSessionProperty("customKey", "customValue").build()
  }
  else
  {
    // Set outcome to "false". If supported by the UI, the error message is displayed:
    action = fr.Action.goTo("false").withErrorMessage("Friendly error description.").build()
  }
  ```

  |   |                                                                                                                                                                                           |
  | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | You can also use the `Action` interface for other functionality:- [Setting session properties](#scripting-api-node-sessionProperties)

  - [Using Callbacks](#scripting-api-node-callbacks) |

  For more information on the `Action` interface, refer to [Action](../_attachments/apidocs/org/forgerock/openam/auth/node/api/Action.html) in the *AM Javadoc*.

|   |                                                                                                                                                                                                                                     |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | An outcome specified as an `Action` takes precedence over the value set for the `outcome` variable:```javascript
action = Action.goTo("false").build() // Tree continues along "false" outcome.

outcome = "true" // No effect.
``` |

For more information on specifying outcomes when using the scripted decision node, refer to [Scripted Decision node](auth-node-configuration-hints.html#auth-node-scripted-decision).

The following table lists the bindings accessible to scripted decision node scripts:

**Scripted decision node bindings**

| Binding             | Information                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auditEntryDetail`  | Add information to the AM audit logs.Refer to [Adding audit information](#scripting-api-node-audit-logging).                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `callbacks`         | Request additional data from the user, by sending any of the supported callbacks.Refer to [Using Callbacks](#scripting-api-node-callbacks).                                                                                                                                                                                                                                                                                                                                                                                       |
| `existingSession`   | If the user has previously authenticated and has a session, use this variable to access the properties of that session.The user will only have an existing session when performing a session upgrade. Any properties that may have been added by nodes earlier in the current tree will not appear on the user's new session until the authentication tree is completed, and are therefore not available to the `existingSession` variable.Refer to [Accessing existing session properties](#scripting-api-node-existingSession). |
| `httpClient`        | Make outbound HTTP calls.Refer to [Accessing HTTP Services](../scripting-guide/scripting-api-global-http-client.html).                                                                                                                                                                                                                                                                                                                                                                                                            |
| `idRepository`      | Access the data stored in the user's profile.Refer to [Accessing profile data](#scripting-api-node-id-repo).                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `logger`            | Write information to the AM debug logs.Refer to [Debug logging](../scripting-guide/scripting-api-global-logger.html).                                                                                                                                                                                                                                                                                                                                                                                                             |
| `realm`             | Return the name of the realm to which the user is authenticating as a string.For example, authenticating to the top-level realm returns a string value of `/` (forward-slash). Authenticating to a subrealm of the top level realm might return `/subRealm`.                                                                                                                                                                                                                                                                      |
| `requestHeaders`    | Access the HTTP headers provided in the login request.Refer to [Accessing request header data](#scripting-api-node-requestHeaders).                                                                                                                                                                                                                                                                                                                                                                                               |
| `requestParameters` | Access the HTTP request parameters provided in the login request.Refer to [Accessing request parameter data](#scripting-api-node-requestParameters).                                                                                                                                                                                                                                                                                                                                                                              |
| `secrets`           | Access the secrets configured in an AM instance.Refer to [Accessing credentials and secrets](#scripting-api-authn-secrets).                                                                                                                                                                                                                                                                                                                                                                                                       |
| `nodeState`         | Access data set by previous nodes in the tree, or store data to be used by subsequent nodes.Refer to [Accessing shared state data](#scripting-api-node-nodeState).                                                                                                                                                                                                                                                                                                                                                                |

## Accessing request header data

Scripted decision node scripts can access the headers provided by the login request by using the methods of the `requestHeaders` object.

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

### Methods

* `String[] requestHeaders.get(String HeaderName)`

  Return a [java.util.ArrayList](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ArrayList.html) of the values in the named request header, or `null`, if the property is not set. Note that header names are case-sensitive.

  Example:

  ```javascript
  var headerName = "user-agent"

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

## Accessing request parameter data

Scripted decision node scripts can access any query parameters provided by the login request by using the methods of the `requestParameters` object.

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

### Methods

* `String[] requestParameters.get(String ParameterName)`

  Return a [java.util.ArrayList](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ArrayList.html) of the values in the named request parameter, or `null`, if the parameter is not available.

  Example:

  * JavaScript

  * Groovy

  ```javascript
  var service
  var authIndexType = requestParameters.get("authIndexType")

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

  ```groovy
  def service
  def authIndexType = requestParameters.get("authIndexType")

  if (authIndexType && authIndexType.get(0) == "service") {
      service = requestParameters.get("authIndexValue").get(0)
  }
  ```

  |   |                                                                                                                                                                                                             |
  | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | In JavaScript, the values stored in `requestParameters` have a `typeof` of *object*, and represent the `java.lang.String` class. Convert the value to a string in order to use strict equality comparisons. |

## Accessing shared state data

A script can access the shared state of the tree 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 doesn't need to be encrypted. The transient state is for sensitive data and for data not required after the next callback.

* 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. |

### Methods

* \`JsonValue nodeState.get(\`String *Property Name*) \`

  Returns the value of the named property. The value may come from the transient, secure, or shared states, in that order. For example, if the same property is available in several states, the method will return the value of the property in the transient state first.

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

  Note that property names are case-sensitive.

  Example:

  ```javascript
  var currentAuthLevel = nodeState.get("authLevel").asString()
  var givenPassword = nodeState.get("password").asString()
  ```

* `nodeState nodeState.putShared(String PropertyName, String ProperyValue)`

  Sets the value of the named shared state property. Note that property names are case-sensitive.

  Example:

  ```javascript
  try {
    var currentAuthLevel = nodeState.get("authLevel").asString()
  } catch (e) {
    nodeState.putShared("errorMessage", e.toString())
  }
  ```

* `nodeState nodeState.putTransient(String PropertyName, String ProperyValue)`

  Sets the value of the named transient state property. Note that property names are case-sensitive.

  Example:

  ```javascript
  nodeState.putTransient("sensitiveKey", "sensitiveValue")
  ```

## Accessing profile data

Scripted decision nodes can access profile data through the methods of the `idRepository` object.

### Methods

* `Set idRepository.getAttribute(String UserName, String AttributeName)`

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

* `Void idRepository.setAttribute(String UserName, String AttributeName, Array Attribute Values)`

  Set the named attribute as specified by the attribute value for the named user, and persist the result in the user's profile.

  Example:

  * JavaScript

  * Groovy

  ```javascript
  var username = nodeState.get("username")
  var attribute = "mail"

  idRepository.setAttribute(username, attribute, ["user.0@a.com", "user.0@b.com"])
  ```

  ```groovy
  def username = nodeState.get("username")
  def attribute = "mail"

  idRepository.setAttribute(username, attribute, ["user.0@a.com", "user.0@b.com"] as String[])
  ```

* `Void idRepository.addAttribute(String UserName, String AttributeName, String Attribute Value)`

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

  Example:

  ```javascript
  var username = nodeState.get("username")
  var attribute = "mail"

  // Add a value as a String.
  idRepository.addAttribute(username, attribute, "user.0@c.com")
  logger.error(idRepository.getAttribute(username, attribute).toString())
  // > ERROR: [user.0@a.com, user.0@c.com]

  // Get the first value.
  logger.error(idRepository.getAttribute(username, attribute).iterator().next())
  // > ERROR: user.0@a.com

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

  logger.error(idRepository.getAttribute(username, "non-existing-attribute").toString())
  // > ERROR: []: If no attribute by this name is found, an empty Set is returned.
  ```

## Setting session properties

Scripted Decision Node scripts can create session properties by using the `Action` interface. The following examples set the outcome to `true`, and add a custom session property:

* JavaScript

* Groovy

```javascript
var goTo = org.forgerock.openam.auth.node.api.Action.goTo

action = goTo("true").putSessionProperty("mySessionProperty","myPropertyValue").build()
```

```groovy
import org.forgerock.openam.auth.node.api.Action

action = Action.goTo("true").putSessionProperty("mySessionProperty","myPropertyValue").build()
```

|   |                                                                                                                                                                                                                                                                                                                                     |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Add the property name to the Allowlisted Session Property Names list in the Session Property Whitelist Service; otherwise, it will not be added to sessions.For more information on this service, refer to [Session Property Whitelist Service](../reference/global-services-configuration.html#global-amsessionpropertywhitelist). |

Add the script to a scripted decision node in your authentication tree. Users that authenticate successfully using that tree will have the property added to their session, as shown in the following output when [introspecting a session](../sessions-guide/managing-sessions-REST.html#rest-api-session-information):

```json
{
    "username": "15249a65-8f9a-4063-9586-a2465963cee4",
    "universalId": "id=15249a65-8f9a-4063-9586-a2465963cee4,ou=user,o=alpha,ou=services,ou=am-config",
    "realm": "/alpha",
    "latestAccessTime": "2020-10-22T15:01:14Z",
    "maxIdleExpirationTime": "2020-10-22T15:31:14Z",
    "maxSessionExpirationTime": "2020-10-22T17:01:13Z",
    "properties": {
        "AMCtxId": "dffed74d-f203-469c-9ed2-34738915baea-5255",
        "mySessionProperty": "myPropertyValue"
    }
}
```

## Accessing existing session properties

Scripted Decision Node scripts can access any existing session properties during a session upgrade request, by using the `existingSession` object.

The following table lists the methods of the `existingSession` object:

### Methods

* `String existingSession.get(String _Property Name)`

  Return the string value of the named existing session property, or `null`, if the property is not set. Note that property names are case-sensitive.

  |   |                                                                                                                                                                                                             |
  | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | 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:

  ```javascript
  if (typeof existingSession !== 'undefined')
  {
      existingAuthLevel = existingSession.get("AuthLevel")
  }
  else
  {
      logger.error("Variable existingSession not declared - not a session upgrade.")
  }
  ```

## Using Callbacks

The scripted decision node can use callbacks to provide or request additional information during the authentication process.

Example:

The following scripts use the `NameCallBack` callback to request a "Nickname" value from the user, and adds the returned value to the `nodeState` object for use elsewhere in the authentication tree:

* Groovy

* JavaScript

```groovy
import org.forgerock.openam.auth.node.api.*
import javax.security.auth.callback.NameCallback

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

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

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

For a list of supported callbacks, refer to [Supported callbacks](authn-supported-callbacks.html).

## Accessing credentials and secrets

Scripts used in a scripted decision node can access the secrets configured in [AM secret stores](../security-guide/secrets-certs-keys.html) *on the file system*.

For example, a script can access credentials or secrets defined in a file system secret volume to make outbound calls to a third-party REST service, without hardcoding those credentials in the script.

### Methods

* `String secrets.getGenericSecret(String Secret ID)`

  Returns the value of the specified secret ID.

  If the secret ID is defined at the realm level, its value is returned; otherwise, the script returns the value defined at the global level.

  Only secret IDs that begin with the string `scripted.node.` are accessible to scripts.

  For more information on creating secret IDs in a secret store, refer to [Secret stores](../security-guide/secret-stores.html).

  Use the following functions to format the returned secret value:

  * `getAsBytes()`

    Retrieve the secret value in `byte[]` format.

  * `getAsUtf8()`

    Retrieve the secret value in UTF-8 format.

    Example:

    The following example scripts show how to get the value (`passwd`) from a secret ID named `scripted.node.secret.id`. They use the value in a basic authentication header to access the [http://httpbin.org/basic-auth/{user}/{passwd}](http://httpbin.org/#/Auth/get_basic_auth__user__passwd_) service:

    * JavaScript

    * Groovy

    ```javascript
    var username = "demoUser"
    var password = secrets.getGenericSecret("scripted.node.secret.id").getAsUtf8()

    var auth = java.util.Base64.getEncoder().encodeToString(java.lang.String(username + ":" + password).getBytes())

    var request = new org.forgerock.http.protocol.Request()
    request.setMethod("GET")
    request.setUri("http://httpbin.org/basic-auth/demoUser/passwd")
    request.getHeaders().add("content-type","application/json; charset=utf-8")
    request.getHeaders().add("Authorization", "Basic " + auth)

    var response = httpClient.send(request).get()
    var jsonResult = JSON.parse(response.getEntity().getString())
    logger.error("Script result: " + JSON.stringify(jsonResult))
    if (jsonResult.hasOwnProperty("authenticated")) {
        logger.error("outcome = success")
        outcome = "success"
    }  else {
        logger.error("outcome = failure")
        outcome = "failure"
    }
    ```

    ```groovy
    def username = "demoUser"
    def password = secrets.getGenericSecret("scripted.node.secret.id").getAsUtf8()

    def auth = java.util.Base64.getEncoder().encodeToString((username + ":" + password).getBytes())

    def request = new org.forgerock.http.protocol.Request()
    request.setMethod("GET")
    request.setUri("http://httpbin.org/basic-auth/demoUser/passwd")
    request.getHeaders().add("content-type","application/json; charset=utf-8")
    request.getHeaders().add("Authorization", "Basic " + auth)

    def response = httpClient.send(request).get()
    if (response.status.successful) {
        logger.error("outcome = success")
        outcome = "success"
    }  else {
        logger.error("outcome = failure")
        outcome = "failure"
    }
    ```

|   |                                                                                                                                                                                                                                                                                                                                                |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | To use these sample scripts, you may need to add the following classes to the class allowlist property in the `AUTHENTICATION_TREE_DECISION_NODE` scripting engine configuration:* `org.mozilla.javascript.ConsString`

* `java.util.Base64`

* `java.util.Base64$Encoder`Refer to [Security](../scripting-guide/script-engine-security.html). |

## Adding audit information

Use a script to add information to audit log entries with the `auditEntryDetail` variable.

AM appends the value of the variable to the [authentication audit logs](../security-guide/sec-maint-audit-ref.html#authentication-log-format).

For JavaScript, the variable must be a string. This example adds the username and email address to the log entry:

```javascript
var username = nodeState.get("username").asString();
var email = idRepository.getAttribute(username,"mail").iterator().next().toString();

var detailStr = ("Extra Audit: [" + username + "] Email address: " + email).toString();
auditEntryDetail= detailStr;

outcome = "true";
```

AM records the audit string in the `entries > info > nodeExtraLogging > auditInfo` field of the authentication log entry:

```json
{
    "_id": "31a9caa2-1439-4088-813f-a60c5f083a45-7725962",
    "timestamp": "2022-12-13T17:22:14.818Z",
    "eventName": "AM-NODE-LOGIN-COMPLETED",
    "transactionId": "31a9caa2-1439-4088-813f-a60c5f083a45-7725948",
    "trackingIds": [
        "31a9caa2-1439-4088-813f-a60c5f083a45-7725576"
    ],
    "principal": [
        "bjensen"
    ],
    "entries": [
        {
            "info": {
                "nodeOutcome": "true",
                "treeName": "Example",
                "displayName": "Audit Entry",
                "nodeType": "ScriptedDecisionNode",
                "nodeId": "275a8aeb-7a61-4c70-acc2-decb1e0bc139",
                "authLevel": "0",
                "nodeExtraLogging": {
                    "auditInfo": "Extra Audit: [bjensen] Email address: bjensen@example.com"
                }
            }
        }
    ],
    "realm": "/alpha",
    "component": "Authentication"
}
```

For more information about auditing, refer to [Audit logging](../security-guide/audit-logging.html).
