---
title: Scripted Decision node API
description: 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.
component: pingoneaic
page_id: pingoneaic:am-scripting:scripting-api-node
canonical_url: https://docs.pingidentity.com/pingoneaic/am-scripting/scripting-api-node.html
keywords: ["Authentication", "Sessions", "Nodes &amp; Trees", "Scripting"]
page_aliases: ["authentication-guide:scripting-api-node.adoc", "am-authentication:scripting-api-node.adoc", "release-notes:rapid-channel/scripted-decision-node-suspend.adoc", "release-notes:rapid-channel/samlapp-binding.adoc"]
section_ids:
  scripted-decision-node-bindings: Script bindings
  action-set-outcome: Set script outcome and script attributes
  scripting-api-node-requestCookies: Access request cookies
  scripting-api-node-requestHeaders: Access request header
  scripting-api-node-requestParameters: Access request parameters
  scripting-api-node-nodeState: Access shared state data
  scripting-api-node-id-repo: Access profile data
  methods: Methods
  examples: Examples
  scripting-api-node-cachemanager: Cache script values
  scripting-api-node-sessionProperties: Set session properties
  oauthapp-binding: Query OAuth 2.0 application and authorization request
  scripting-api-node-session-timeouts: Set authenticated session timeouts
  samlapp-binding: Query SAML application and authentication request
  scripting-api-node-existingSession: Existing session properties
  scripting-api-node-lockout-message: Customize account lockout message
  scripting-api-node-callbacks: Use callbacks
  scripting-api-node-suspend: Suspend and resume journeys
  scripting-api-node-audit-logging: Add information to authentication audit log entries
---

# Scripted Decision node API

A [Scripted Decision node](https://docs.pingidentity.com/auth-node-ref/latest/scripted-decision.html) 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.

[Create or edit a journey decision node script](../developer-docs/scripting-auth.html#decision-scripts) directly from within in the journey editor.

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, which can be either a String or a CharSequence type, aren't limited to `true` and `false`. Specify a value for each possible outcome and as many outcomes as required by the journey.

Use the `action` object to [set the outcome](#action-set-outcome).

## Script bindings

A Scripted Decision node script has access to some or all the common *bindings*, predefined objects that Advanced Identity Cloud injects into the script execution context, depending on the [script engine type](scripting-env.html).

You can find information and examples for these bindings, such as `httpClient`, `logger`, `openidm`, and `utils`, in [Common bindings](script-bindings.html).

In addition to the common bindings, a Scripted Decision node script has access to the following specific bindings.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Advanced Identity Cloud has introduced a [next-generation scripting engine](next-generation-scripts.html) that offers several benefits, including enhanced script bindings.The availability and usage of bindings depend on the script engine version of the script: legacy or next-generation. Both versions are described in this section.You can find information about migrating to the enhanced scripting engine in [Migrate to next-generation scripts](next-generation-scripts.html#migrate-to-v2-steps). |

| Binding                           | Description                                                                                                                                       | Further information                                                                                                                                                                                                                                                                                                                        |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `action` (1)                      | Set the script outcome and perform script-related actions.                                                                                        | [Set script outcome and script attributes](#action-set-outcome)[Set session properties](#scripting-api-node-sessionProperties)[Set authenticated session timeouts](#scripting-api-node-session-timeouts)[Customize account lockout message](#scripting-api-node-lockout-message)[Suspend and resume journeys](#scripting-api-node-suspend) |
| `auditEntryDetail`                | Add information to the Advanced Identity Cloud audit logs.                                                                                        | [Add information to authentication audit log entries](#scripting-api-node-audit-logging)                                                                                                                                                                                                                                                   |
| `cacheManager`(1)                 | Cache script values.                                                                                                                              | [Cache script values](#scripting-api-node-cachemanager)                                                                                                                                                                                                                                                                                    |
| `callbacksBuilder`(1) `callbacks` | Request additional data from the user using a callback.                                                                                           | [Use callbacks](#scripting-api-node-callbacks)                                                                                                                                                                                                                                                                                             |
| `existingSession`                 | If the user has previously authenticated and has an authenticated session, use this binding to access the properties of that session.             | [Existing session properties](#scripting-api-node-existingSession)                                                                                                                                                                                                                                                                         |
| `idRepository`                    | Access the data stored in the user's profile.                                                                                                     | [Access profile data](#scripting-api-node-id-repo)                                                                                                                                                                                                                                                                                         |
| `nodeState`                       | Access data set by previous nodes in the journey or store data to be used by subsequent nodes.                                                    | [Access shared state data](#scripting-api-node-nodeState)                                                                                                                                                                                                                                                                                  |
| `oauthApplication`(1)             | Access information about the OAuth 2.0 request if the Scripted Decision node is part of a federated journey associated with a client application. | [Query OAuth 2.0 application and authorization request](#oauthapp-binding)                                                                                                                                                                                                                                                                 |
| `requestCookies`                  | Access the request cookies in the login request.                                                                                                  | [Access request cookies](#scripting-api-node-requestCookies)                                                                                                                                                                                                                                                                               |
| `requestHeaders`                  | Access the HTTP headers provided in the login request.                                                                                            | [Access request header](#scripting-api-node-requestHeaders)                                                                                                                                                                                                                                                                                |
| `requestParameters`               | Access the HTTP request parameters provided in the login request.                                                                                 | [Access request parameters](#scripting-api-node-requestParameters)                                                                                                                                                                                                                                                                         |
| `resumedFromSuspend`              | Boolean to indicate whether evaluation has resumed after suspension.Use with the `action` binding to suspend and resume journeys.                 | [Suspend and resume journeys](#scripting-api-node-suspend)                                                                                                                                                                                                                                                                                 |
| `samlApplication`(1)              | Access information about the SAML 2.0 request if the Scripted Decision node is part of a federated journey associated with an application.        | [Query SAML application and authentication request](#samlapp-binding)                                                                                                                                                                                                                                                                      |

(1) Not available in [Legacy](next-generation-scripts.html) scripts.

### Set script outcome and script attributes

Use the `action` object to define the outcome and access functions to set script attributes, such as error or lockout messages, verified identities, or session properties.

|   |                                                                                                                                                                                                                               |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Use `action` instead of `outcome` to define the exit path from the node. The `action` object takes precedence and overrides the value set for the `outcome` variable. You can also use it to perform other script operations. |

* Next-generation

* Legacy

Call the `goTo` method directly on the `action` binding. Optionally, chain with `ActionWrapper` functions.

> **Collapse: Supported  functions:**
>
> | Method                                                                                                                 | Information                                                                                                                                                                    |
> | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
> | `public ActionWrapper putSessionProperty(String key, String value)`                                                    | Add a session property.Learn more in [Set session properties](#scripting-api-node-sessionProperties).                                                                          |
> | `public ActionWrapper removeSessionProperty(String key)`                                                               | Remove an existing session property.                                                                                                                                           |
> | `public ActionWrapper withDescription(String description)`                                                             | Set a description for this action.                                                                                                                                             |
> | `public ActionWrapper suspend(String message, SuspensionLogic logic, int maximumSuspendDuration)`                      | Suspend and resume an authentication journey.Learn more in [Suspend and resume journeys](#scripting-api-node-suspend).                                                         |
> | `public ActionWrapper withErrorMessage(String message)`                                                                | Set an error message to display to the end user when the journey reaches the [Failure node](https://docs.pingidentity.com/auth-node-ref/latest/failure.html).                  |
> | `public ActionWrapper withHeader(String header)`                                                                       | Set a header for this action.                                                                                                                                                  |
> | `public ActionWrapper withIdentifiedAgent(String agentName)``public ActionWrapper withIdentifiedUser(String username)` | 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.                   |
> | `public ActionWrapper withLockoutMessage(String message)`                                                              | Set an error message to display to the end user when the account is locked or inactive.Learn more in [Customize account lockout message](#scripting-api-node-lockout-message). |
> | `public ActionWrapper withMaxIdleTime(int maxIdleTime)`                                                                | Set the maximum authenticated idle time.Learn more in [Set authenticated session timeouts](#scripting-api-node-session-timeouts).                                              |
> | `public ActionWrapper withMaxSessionTime(int maxSessionTime)`                                                          | Set the maximum authenticated session time.Learn more in [Set authenticated session timeouts](#scripting-api-node-session-timeouts).                                           |
> | `public ActionWrapper withStage(String stage)`                                                                         | Set a stage name to return to the client to aid the rendering of the UI.                                                                                                       |

```javascript
// Evaluation continues along the "false" outcome
action.goTo("false").withErrorMessage(error);

 // No effect
outcome = "true";
```

Import the `Action` class to access the `goTo` method. Optionally, chain with `ActionBuilder` functions.

`public static ActionBuilder goTo(String outcome)`

> **Collapse: Supported  functions:**
>
> | Method                                                                                                                                | Information                                                                                                                                                   |
> | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | `public ActionBuilder putSessionProperty(String key, String value)`                                                                   | Add a session property.Learn more in [Set 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 this action.                                                                                                                            |
> | `public ActionBuilder withErrorMessage(String message)`                                                                               | Set an error message to display to the end user when the journey reaches the [Failure node](https://docs.pingidentity.com/auth-node-ref/latest/failure.html). |
> | `public ActionBuilder withHeader(String header)`                                                                                      | Set a header for this action.                                                                                                                                 |
> | `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.  |
> | `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.                        |

You can find information on all supported methods in [ActionBuilder](../_attachments/apidocs/org/forgerock/openam/auth/node/api/Action.ActionBuilder.html).

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

// Evaluation continues along the "false" outcome
action = fr.Action.goTo("false").withErrorMessage(error).build();

 // No effect
outcome = "true";
```

A script can also use the `action` object to set [session properties](#scripting-api-node-sessionProperties) and [callbacks](#scripting-api-node-callbacks).

### Access request cookies

Use the `requestCookies` binding to access the cookies in the login request without having to perform string manipulation of the request header.

The `requestCookies` binding stores the cookie names and values in the format `Map<String, String>`.

* Next-generation

* Legacy

```javascript
if (typeof requestCookies === "undefined") {
  action.goTo("false");
}

if (Object.keys(requestCookies).length === 0) {
  action.goTo("false");
}

if (requestCookies.containsKey("amlbcookie")) {
  logger.debug("amlbcookie: " + requestCookies.amlbcookie);
}

action.goTo("true");
```

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

var cookieHeader = requestHeaders.get("Cookie");
var cookies = cookieHeader.get(0).split(";");
var cookieValue = null;

cookies.forEach(
  function(cookie) {
    var cookieSpec = cookie.split("=");
    var cookieName = cookieSpec[0].trim();

    nodeState.putShared("cookieName", cookieName);

    if (cookieName == "amlbcookie") {
      cookieValue = cookieSpec[1].trim();
      logger.message("amlbcookie: " + cookieValue);
    }
  }
)
if(cookieValue == null) {
  action = fr.Action.goTo("false").build();
}
action = fr.Action.goTo("true").build();
```

### Access request header

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.

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

  * Next-generation

  * Legacy

  ```javascript
  if (requestHeaders.get("user-agent").get(0).indexOf("Chrome") !== -1) {
      action.goTo("true");
  } else {
      action.goTo("false");
  }
  ```

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

  if (requestHeaders.get("user-agent").get(0).indexOf("Chrome") !== -1) {
      action = fr.Action.goTo("true").build();
  } else {
      action = fr.Action.goTo("false").build();
  }
  ```

### Access request parameters

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 doesn't affect the request.

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

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

  * Next-generation

  * Legacy

  ```javascript
  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);
  }
  action.goTo("true");
  ```

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

  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);
  }
  action = fr.Action.goTo("true").build();
  ```

  |   |                                                                                                                                                           |
  | - | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | In JavaScript, the data type (`typeof`) of `requestParameters` values is *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.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | The `sharedState` and `transientState` bindings are deprecated, and are removed in the [next-generation](next-generation-scripts.html) scripting engine.You can still access the different states with the `nodeState` object, which replaces the `sharedState` and `transientState` bindings.For example, use `nodeState.putShared()` instead of `sharedState.put()`, and `nodeState.putTransient()` instead of `transientState.put()`. Use `nodeState.get()` to retrieve shared and transient state properties. |

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

* `<returnvalue> 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 isn't set, the method returns `null`.

  * Next-generation

  * Legacy

  Returns the value as an `Object`. The JavaScript return type is coerced, so you no longer need to call functions such as `asString()` or `asMap()`.

  ```javascript
  var currentAuthLevel = nodeState.get("authLevel");
  var thePassword = nodeState.get("password");

  action.goTo("true");
  ```

  Returns the value as a `JsonValue`. Use functions such as `asString()` or `asMap()` to convert to the expected type.

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

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

  action = fr.Action.goTo("true").build();
  ```

* `<returnvalue> nodeState.getObject(String propertyName)`

  Returns the value of the named property.

  If the value is a map, this method combines and returns the values stored in transient, secure, and shared state.

  If the property isn't set, the method returns `null`.

  |   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
  | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | The `getObject` method returns an immutable object. If you want to update the object, use `nodeState.get(String propertyName)` instead. The `get` method doesn't combine values but returns the first value found in transient, secure, or shared state (in that order). You can then modify the returned object.For example:```javascript
  var attributes = nodeState.get("objectAttributes");
  if (attributes) {
    attributes.put("mail", "test@example.com");
    nodeState.putShared("objectAttributes", attributes);
  }

  action.goTo("true");
  ``` |

  * Next-generation

  * Legacy

  Returns the value as an `Object`. The JavaScript return type is coerced.

  ```javascript
  var attributes = nodeState.getObject("objectAttributes");

  action.goTo("true");
  ```

  Returns the value as a `JsonValue`. Use functions such as `asString()` or `asMap()` to convert to the expected type.

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

  var attributes = nodeState.getObject("objectAttributes").asMap();

  action = fr.Action.goTo("true").build();
  ```

* `<returnvalue> nodeState.putShared(String propertyName, String propertyValue)`

  Sets the value of the named shared state property.

  * Next-generation

  * Legacy

  Returns a modified instance of `NodeStateScriptWrapper`.

  ```javascript
  try {
    var thePassword = nodeState.get("password");
  } catch (e) {
    nodeState.putShared("errorMessage", e.toString());
  }

  action.goTo("true");
  ```

  Returns a modified instance of `NodeState`.

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

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

  action = fr.Action.goTo("true").build();
  ```

* `<returnvalue> nodeState.putTransient(String propertyName, String propertyValue)`

  Sets the value of the named transient state property.

  * Next-generation

  * Legacy

  Returns a modified instance of `NodeStateScriptWrapper`.

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

  action.goTo("true");
  ```

  Returns a modified instance of `NodeState`.

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

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

  action = fr.Action.goTo("true").build();
  ```

* `<returnvalue> nodeState.mergeShared(Map<String, Object> object)`

  Adds the specified object to shared state by merging the keys. If a key already exists, *in any state*, its value is replaced by the specified value.

  You can only merge a nested object if it's a registered state object, such as `objectAttributes`. Nested objects are allowed *inside* `objectAttributes` but won't be merged.

  If you provide new values for `objectAttributes`, they're merged with the existing values. For example, if the following `objectAttributes` values already exist in *secure* state:

  ```
  { "key1": "a", "key2": "b" }
  ```

  and you call `mergeShared()` with this set of values:

  ```
  { "key1": "z", "key3": "c"}
  ```

  then the merged result contains these values when you call `getObject("objectAttributes")`:

  ```
  "objectAttributes": { "key1": "z", "key2": "b", "key3": "c"}
  ```

  where `key1` is removed from secure and added to shared state along with `key3`, and `key2` remains in secure state.

  * Next-generation

  * Legacy

  Returns a modified instance of `NodeStateScriptWrapper`.

  ```javascript
  // specify new values for objectAttributes as a JSON object
  nodeState.mergeShared({
    "objectAttributes": {
      "mail": "bjensen@example.com",
      "userName": "bjensen"
    }
  })

  action.goTo("true");
  ```

  Returns a modified instance of `NodeState`.

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

  // specify new values for objectAttributes as a JSON object
  nodeState.mergeShared({
    "objectAttributes": {
      "mail": "bjensen@example.com",
      "userName": "bjensen"
    }
  })

  action = fr.Action.goTo("true").build();
  ```

* `<returnvalue> nodeState.mergeTransient(Map<String, Object> object)`

  Adds the specified object to transient state by merging the keys. If a key already exists, *in any state*, its value is replaced by the specified value.

  You can only merge a nested object if it's a registered state object, such as `objectAttributes`. Nested objects are allowed *inside* `objectAttributes` but won't be merged.

  If you provide new values for `objectAttributes`, they're merged with the existing values. For example, if the following `objectAttributes` values already exist in *secure* state:

  ```
  { "key1": "a", "key2": "b" }
  ```

  and you call `mergeTransient()` with this set of values:

  ```
  { "key1": "z", "key3": "c"}
  ```

  then the merged result contains these values when you call `getObject("objectAttributes")`:

  ```
  "objectAttributes": { "key1": "z", "key2": "b", "key3": "c"}
  ```

  where `key1` is removed from secure and added to transient state along with `key3`, and `key2` remains in secure state.

  * Next-generation

  * Legacy

  Returns a modified instance of `NodeStateScriptWrapper`.

  ```javascript
  // specify new values for objectAttributes as a JSON object
  nodeState.mergeTransient({
    "objectAttributes": {
      "mail": "bjensen@example.com",
      "userName": "bjensen"
    }
  })

  action.goTo("true");
  ```

  Returns a modified instance of `NodeState`.

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

  // specify new values for objectAttributes as a JSON object
  nodeState.mergeTransient({
    "objectAttributes": {
      "mail": "bjensen@example.com",
      "userName": "bjensen"
    }
  })

  action = fr.Action.goTo("true").build();
  ```

### Access profile data

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

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | In these examples, an [Identity Store Decision node](https://docs.pingidentity.com/auth-node-ref/latest/cloud/identity-store-decision.html) with Username as Universal Identifier enabled precedes the [Scripted Decision node](https://docs.pingidentity.com/auth-node-ref/latest/scripted-decision.html) that uses the script. As a result, when you call `nodeState.get('username')` the function returns the user's `_id`, which is required to get the identity object. |

#### Methods

* Next-generation

* Legacy

```javascript
// Methods for ScriptedIdentity object returned by idRepository
// var identity = idRepository.getIdentity(uuid)

List<String> getAttributeValues(String attributeName);
String getName();
String getUniversalId();
void setAttribute(String attributeName, List<String> attributeValues);
void addAttribute(String attributeName, String attributeValue);
void store() throws IdentityUpdateException;
boolean exists();
```

```javascript
// Methods accessed directly through idRepository binding
Set idRepository.getAttribute(String username, String attrName)
void idRepository.setAttribute(String username, String attrName, Array attrValues)
void idRepository.addAttribute(String username, String attrName, String attrValue)
```

#### Examples

* Get attribute values

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

  * Next-generation

  * Legacy

  ```javascript
  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username");
  var attribute = "mail";

  // Get identity object separately
  var identity = idRepository.getIdentity(uuid);

  if (identity.exists() !== false) {

    // Returns all values as an array: ["test@example.com", "user@example.com"]
    var mailAttrs = identity.getAttributeValues(attribute);

    // Returns the first value: test@example.com
    var mail = identity.getAttributeValues(attribute)[0];

    // If no attribute by this name is found, the result is an empty array: []
    var noAttrs = identity.getAttributeValues("non-existent-attribute");

    // Get custom attributes stored as JSON in the attribute `fr-idm-custom-attrs`
    var customAttrsRaw = identity.getAttributeValues('fr-idm-custom-attrs')[0];

    // Returns custom attributes as an object: {"custom_device": "mobile", "custom_locale": "en-US"}
    var customAttrs = JSON.parse(customAttrsRaw);

    // Returns the value of a specific custom attribute: en-US
    var locale = customAttrs.custom_locale;

    action.goTo("true");
  } else action.goTo("false");
  ```

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

  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username").asString();
  var attribute = "mail";

  // Returns all values, for example: [test@example.com, user@example.com]
  var mailAttrs = idRepository.getAttribute(uuid, attribute).toString();

  // Returns the first value, for example: test@example.com
  var mail = idRepository.getAttribute(uuid, attribute).iterator().next();

  // Returns a value at the specified index, for example: user@example.com
  idRepository.getAttribute(uuid, attribute).toArray()[1];

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

  // Get all custom attributes, which are stored as JSON in the single attribute `fr-idm-custom-attrs`
  var customAttrsRaw = idRepository.getAttribute(uuid, 'fr-idm-custom-attrs').toArray()[0];

  // Returns all custom attributes as an object, for example: {"custom_device": "mobile", "custom_locale": "en-US"}
  var customAttrs = JSON.parse(customAttrsRaw);

  // Returns the value of a specific custom attribute, for example: en-US
  var locale = customAttrs.custom_locale;

  action = fr.Action.goTo("true").build();
  ```

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

  * Next-generation

  * Legacy

  ```javascript
  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username");

  // Get identity separately
  var identity = idRepository.getIdentity(uuid);

  if (identity.exists() !== false) {

    // Set the attribute directly on the identity object
    identity.setAttribute("mail", ["test@example.com"]);

    try {
      // Explicitly persist data
      // throws an exception if setAttribute failed
      identity.store();
      action.goTo("true");
    } catch(e) {
    logger.error("Unable to persist attribute. " + e);
    action.goTo("false");
    }
  } else action.goTo("false");
  ```

  |   |                                                                            |
  | - | -------------------------------------------------------------------------- |
  |   | You must explicitly call `store()` to persist changes to attribute values. |

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

  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username").asString();

  // Set attribute using the idRepository object
  idRepository.setAttribute(uuid, "mail", ["test@example.com"]);

  try {
    // check setAttribute was successful
    var mail = idRepository.getAttribute(uuid, "mail").iterator().next();
    action = fr.Action.goTo("true").build();
  } catch(e) {
    logger.error("Unable to persist attribute. " + e);
    action = fr.Action.goTo("false").build();
  }
  ```

* Add attribute values

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

  * Next-generation

  * Legacy

  ```javascript
  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username");

  // Get identity separately
  var identity = idRepository.getIdentity(uuid);

  if (identity.exists() !== false) {

    // Add a value as a string.
    identity.addAttribute("mail", "user@example.com");

    try {
      // Explicitly persist data
      // throws an exception if addAttribute failed
      identity.store();
      action.goTo("true");
    } catch(e) {
      logger.error("Unable to persist attribute. " + e);
      action.goTo("false");
    }
  } else action.goTo("false");
  ```

  |   |                                                                            |
  | - | -------------------------------------------------------------------------- |
  |   | You must explicitly call `store()` to persist changes to attribute values. |

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

  // Get UUID by enabling `Username as Universal Identifier`
  // in a preceding Identity Store Decision node
  var uuid = nodeState.get("username").asString();
  var attr = "mail";

  // get number of existing attribute values
  var mail = idRepository.getAttribute(uuid, attr).toArray();

  // Add attribute using the idRepository object
  idRepository.addAttribute(uuid, attr, ["test@example.com"]);

  // check addAttribute was successful
  var updatedmail = idRepository.getAttribute(uuid, attr).toArray();
  if (updatedmail.length > mail.length) {
    action = fr.Action.goTo("true").build();
  } else {
    logger.error("Unable to add attribute.");
    action = fr.Action.goTo("false").build();
  }
  ```

### Cache script values

Use the `cacheManager` binding to store values that can persist beyond the duration of a journey.

The binding has the following methods:

* `boolean exists(String cacheName)`

  Returns true if the cache with the specified name exists, false if not.

* `Cache named(String cacheName)`

  Returns the cache with the specified name, null if it doesn't exist.

  You can call the following methods on the `Cache` object:

  * `Object get(Map<String, String> key)`

    Returns the value mapped to the specified key. The key must be a JSON object in the format of a Map of Strings, for example:

    ```javascript
    var key: {
          url: "{idc_example_fqdn}/access_token}",
          clientId: clientId,
          client_secret: "esv.client.secret",
          scope: "profile"
    }
    ```

    The return object is the same format as that returned by the `load()` function in your [cache loading script](cache-manager.html#cache-manager-load-script).

  * `void refresh(Map<String, String> key)`

    Call this function to refresh the cache. The cache invokes the `reload()` function to replace the value for the specified key.

  * `void invalidate(Map<String, String> key)`

    Evict the specified entry from the cache.

  * `void invalidateAll()`

    Call this method to evict all entries and clear the cache.

To use the binding, you must configure an instance of the [Cache Manager](../am-reference/services-configuration.html#cachemanager-service) service and reference a cache loading script.

Find information about how to use the Cache Manager, including a worked example, in [Cache script values](cache-manager.html).

### Set session properties

A script can set session properties with the fields and methods of the [Action](../_attachments/apidocs/org/forgerock/openam/auth/node/api/Action.html) interface.

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

 

* Next-generation

* Legacy

```javascript
action.goTo("true").putSessionProperty("mySessionProperty", "myPropertyValue");
```

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

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

Add the script to a [Scripted Decision node](https://docs.pingidentity.com/auth-node-ref/latest/scripted-decision.html) in your authentication journey.

To verify that the property was set, call the [sessions REST endpoint](../am-sessions/managing-sessions-REST.html#rest-api-session-information) after a user authenticates. If you've added the property name to the [session property allowlist](../am-reference/services-configuration.html#realm-amsessionpropertywhitelist), it appears in the `properties` object of the JSON response. For example:

```json
{
  "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": "2025-07-07T11:03:25Z",
  "maxIdleExpirationTime": "2025-07-07T11:33:25Z",
  "maxSessionExpirationTime": "2025-07-07T13:03:24Z",
  "properties": {
    "AMCtxId": "de5abe95-db97-4354-9d32-aab660ea23a3-4252446",
    "mySessionProperty": "myPropertyValue"
  }
}
```

### Query OAuth 2.0 application and authorization request

The `oauthApplication` binding is present when the Scripted Decision node is part of a journey that runs in an OAuth context and is [associated with an OAuth 2.0 or OIDC application](../am-oauth2/oauth2-register-client.html#oauth2app-journey).

You can make this binding available for *all* OAuth 2.0 and OIDC flows by enabling the application context in the [provider configuration](../am-reference/services-configuration.html#enable-application-context).

Use the `oauthApplication` binding to query the authorization request properties and client application attributes.

You can check and debug the configuration values, but changes to properties are confined to the scope of the script and won't affect the underlying objects.

* String getApplicationId()

  Returns the client application identifier for the OAuth 2.0 / OIDC flow, for example, `myClient`.

* Map\<String, Object> getRequestProperties()

  Returns an object containing the properties of the authorization request.

  > **Collapse: Example request properties**
  >
  > The following example is formatted to display the object structure:
  >
  > ```json
  > {
  >   "requestHeaders": {
  >     "accept": [
  >       "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8"
  >     ],
  >     "accept-encoding": [
  >       "gzip, deflate"
  >     ],
  >     "Accept-Language": [
  >       "en-US,en;q=0.5"
  >     ],
  >     "Connection": [
  >       "keep-alive"
  >     ],
  >     "dnt": [
  >       "1"
  >     ],
  >     "host": [
  >       "am.example.com:8443"
  >     ],
  >     "priority": [
  >       "u=0, i"
  >     ],
  >     "sec-gpc": [
  >       "1"
  >     ],
  >     "upgrade-insecure-requests": [
  >       "1"
  >     ],
  >     "user-agent": [
  >       "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0"
  >     ]
  >   },
  >   "requestParams": {
  >     "scope": [
  >     "openid profile"
  >     ],
  >     "response_type": [
  >       "token id_token"
  >     ],
  >     "redirect_uri": [
  >       "https://www.example.com"
  >     ],
  >     "state": [
  >       "abc123"
  >     ],
  >     "nonce": [
  >       "123abc"
  >     ],
  >     "client_id": [
  >       "myClient"
  >     ]
  >   },
  >   "realm": "/alpha",
  >   "requestUri": "https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/authorize?client_id=myClient&response_type=token%20id_token&scope=openid%20profile&redirect_uri=https://www.example.com&state=abc123&nonce=123abc"
  > }
  > ```

* Map\<String, Object> getClientProperties()

  Returns a map containing information about the client application.

  > **Collapse: Example client properties**
  >
  > ```json
  > {
  >  "clientProperties": {
  >     "allowedGrantTypes": [
  >       "IMPLICIT",
  >       "AUTHORIZATION_CODE"
  >     ],
  >     "clientId": "myClient",
  >     "customProperties": {},
  >     "allowedScopes": [
  >       "openid",
  >       "profile"
  >     ],
  >     "allowedResponseTypes": [
  >       "device_code id_token",
  >       "code token id_token",
  >       "code",
  >       "code id_token",
  >       "device_code",
  >       "id_token",
  >       "code token",
  >       "token id_token",
  >       "token"
  >     ]
  >   }
  > }
  > ```

* Example

  The following script calls the `oauthApplication` methods to log the request and client values:

  * Next-generation

  * Legacy

  ```javascript
  function logObject(objType, obj) {
    var logMsg = objType + " \n";
      for (attr in obj) {
        logMsg += "\t" + attr + " = " + obj[attr] + "\n";
    }
    logger.info(logMsg);
  }

  if (typeof (oauthApplication) === "undefined") {
    logger.error("Tree is not associated with an OIDC application");
    action.goTo("false");
  }
  else {
    logger.info("App Id: " + oauthApplication.getApplicationId());

    logObject("Request", oauthApplication.getRequestProperties());

    logObject("Client", oauthApplication.getClientProperties());

    action.goTo("true");
  }
  ```

  *Not available in Legacy bindings*.

### Set authenticated session timeouts

A script can set the authenticated session timeouts using the `withMaxSessionTime` and `WithMaxIdleTime` methods on the `Action.goto()` object.

When set, they override the [authenticated session timeout settings](../am-sessions/session-state-session-termination.html#auth-session-termination-config) in the journey or the Session service. However, if session timeouts are set for a user, they are always used.

The following example changes the authenticated session timeouts:

 

* Next-generation

* Legacy

```javascript
action.goTo("true").withMaxSessionTime(10).WithMaxIdleTime(5);
```

*Not available in Legacy bindings*.

### Query SAML application and authentication request

Use the `samlApplication` binding to query the SAML 2.0 authentication request properties and the identity provider (IDP) and service provider (SP) configuration attributes.

You can check and debug the configuration values, but changes to properties are confined to the scope of the script and won't affect the underlying objects.

The `samlApplication` binding is present when the Scripted Decision node is part of a journey that meets any of the following conditions:

* Runs in a SAML 2.0 context and is [associated with a SAML 2.0 application](../am-saml2/saml2-providers-and-cots.html).

* It contains a [SAML2 Authentication node](https://docs.pingidentity.com/auth-node-ref/latest/saml2.html) and Advanced Identity Cloud is the SP. The SAML2 Authentication node must precede the Scripted Decision node.

* It is set as [the redirect journey](../am-saml2/configure-providers.html#config-redirect-journey) for an IdP-initiated flow.

You can make the binding available for *all* SAML flows by enabling the application context in the [hosted IdP](../am-saml2/saml2-reference.html#saml-idp-enable-app-context) or [remote SP](../am-saml2/saml2-reference.html#saml-sp-app-context-enabled) entity configuration.

|   |                                                                                                                                                                                      |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | The `samlApplication` binding is currently unavailable in journeys that use custom UIs built with the Ping SDKs. Support for this functionality is planned for a future SDK release. |

* Map\<String, Object> getAssertion()

  Returns the assertion as a map after the [SAML2 Authentication node](https://docs.pingidentity.com/auth-node-ref/latest/saml2.html) completes in a journey where Advanced Identity Cloud is the SP, or set as [the redirect tree](../am-saml2/configure-providers.html#config-redirect-journey) for an IdP-initiated flow.

  The method returns null on the IdP side.

  > **Collapse: Example assertion object**
  >
  > ```json
  > {
  >     "version": "2.0",
  >     "issueInstant": 1751545205000,
  >     "subject": {
  >         "subjectConfirmation": [
  >             {
  >                 "subjectConfirmationData": {
  >                     "elementName": "SubjectConfirmationData",
  >                     "mutable": false,
  >                     "notOnOrAfter": 1751545805000,
  >                     "inResponseTo": "s26958253e04e9cfdbfc0c979d6159000837401b7b",
  >                     "content": [
  >                         ""
  >                     ],
  >                     "recipient": "https://am.example.com/am/Consumer/metaAlias/alpha/sp1",
  >                     "notBefore": null,
  >                     "address": null,
  >                     "contentType": null
  >                 },
  >                 "method": "urn:oasis:names:tc:SAML:2.0:cm:bearer",
  >                 "nameID": null,
  >                 "mutable": false,
  >                 "encryptedID": null,
  >                 "baseID": null
  >             }
  >         ],
  >         "nameID": {
  >             "@class": "com.sun.identity.saml2.assertion.impl.NameIDImpl",
  >             "value": "iPR60UhHDG6EhKsnbbf1MNnLdOuh",
  >             "nameQualifier": "idp1",
  >             "format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
  >             "mutable": false,
  >             "spnameQualifier": "sp1",
  >             "spprovidedID": ""
  >         },
  >         "mutable": false,
  >         "encryptedID": null,
  >         "baseID": null
  >     },
  >     "advice": null,
  >     "signature": "",
  >     "conditions": {
  >         "notOnOrAfter": 1751545805000,
  >         "conditions": [],
  >         "audienceRestrictions": [
  >             {
  >                 "audience": [
  >                     "sp1"
  >                 ],
  >                 "mutable": false
  >             }
  >         ],
  >         "oneTimeUses": [],
  >         "proxyRestrictions": [],
  >         "notBefore": 1751544605000,
  >         "mutable": false
  >     },
  >     "id": "s254b47654db6b3771a81112aae4eee68b98863df3",
  >     "statements": [],
  >     "authnStatements": [
  >         {
  >             "authnContext": {
  >                 "authnContextClassRef": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
  >                 "authnContextDecl": null,
  >                 "authnContextDeclRef": null,
  >                 "authenticatingAuthority": null,
  >                 "mutable": false
  >             },
  >             "subjectLocality": null,
  >             "authnInstant": 1751545204000,
  >             "sessionIndex": "s286b2a8df2df74bcc739daa096621bf1662d20401",
  >             "sessionNotOnOrAfter": null,
  >             "mutable": false
  >         }
  >     ],
  >     "authzDecisionStatements": [],
  >     "attributeStatements": [],
  >     "issuer": {
  >         "value": "idp1",
  >         "nameQualifier": "",
  >         "format": "",
  >         "mutable": false,
  >         "spnameQualifier": "",
  >         "spprovidedID": ""
  >     },
  >     "signed": true,
  >     "mutable": false,
  >     "timeValid": true
  > }
  > ```

* String getApplicationId()

  Returns the application ID (SP entity ID) for the SAML 2.0 flow. You can also get the application ID by calling `getAuthnRequest().get("issuer").get("value")`.

* String getFlowInitiator()

  Returns `IDP` or `SP` depending on which provider initiates the SAML 2.0 flow.

* Map\<String, Object> getAuthnRequest()

  Returns an object containing the properties of the SAML 2.0 authentication request.

  > **Collapse: Example authentication request object**
  >
  > The following example is formatted to display the object structure:
  >
  > ```json
  > {
  >   "destination": "https://<tenant-env-fqdn>/am/SSORedirect/metaAlias/idp1",
  >   "signature": null,
  >   "subject": null,
  >   "issueInstant": 1724341924000,
  >   "consent": "",
  >   "forceAuthn": false,
  >   "protocolBinding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
  >   "mutable": false,
  >   "issuer": {
  >     "value": "sp1",
  >     "nameQualifier": "",
  >     "format": "",
  >     "mutable": false,
  >     "spnameQualifier": "",
  >     "spprovidedID": ""
  >   },
  >   "assertionConsumerServiceURL": "https://<tenant-env-sp-fqdn>/am/Consumer/metaAlias/sp1",
  >   "@class": "com.sun.identity.saml2.protocol.impl.AuthnRequestImpl",
  >   "extensions": null,
  >   "passive": false,
  >   "version": "2.0",
  >   "requestedAuthnContext": {
  >     "@class": "com.sun.identity.saml2.protocol.impl.RequestedAuthnContextImpl",
  >     "elementName": "RequestedAuthnContext",
  >     "mutable": false,
  >     "authnContextClassRef": [
  >       "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
  >     ],
  >     "authnContextDeclRef": [],
  >     "comparison": "exact"
  >   },
  >   "nameIDPolicy": {
  >     "@class": "com.sun.identity.saml2.protocol.impl.NameIDPolicyImpl",
  >     "format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
  >     "allowCreate": true,
  >     "mutable": false,
  >     "spnameQualifier": "sp1"
  >   },
  >   "attributeConsumingServiceIndex": null,
  >   "conditions": null,
  >   "scoping": null,
  >   "signed": false,
  >   "id": "s2c72252b5b2ce2b43e5150c8ee8aba0401f3ef390",
  >   "providerName": "",
  >   "assertionConsumerServiceIndex": null
  > }
  > ```

* Map\<String, List\<String>> getIdpAttributes()

  Returns a map containing the extended configuration for the hosted IDP.

  > **Collapse: Example IDP attributes**
  >
  > | IDP attribute                    | Example value                                                                           |
  > | -------------------------------- | --------------------------------------------------------------------------------------- |
  > | `assertionEffectiveTime`         | `[ "600" ]`                                                                             |
  > | `idpAuthncontextClassrefMapping` | `[ "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\|0\|\|default" ]` |
  > | `assertionNotBeforeTimeSkew`     | `[ "600" ]`                                                                             |
  > | `metaAlias`                      | `[ "/idp1" ]`                                                                           |
  > | `idpECPSessionMapper`            | `[ "com.sun.identity.saml2.plugins.DefaultIDPECPSessionMapper" ]`                       |
  > | `idpAccountMapper`               | `[ "com.sun.identity.saml2.plugins.DefaultIDPAccountMapper" ]`                          |
  > | `nameIDFormatMap`                | `[ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\|mail" ]`                    |
  > | `idpAttributeMapper`             | `[ "com.sun.identity.saml2.plugins.DefaultIDPAttributeMapper" ]`                        |
  > | `saeIDPUrl`                      | `[ "https://<tenant-env-fqdn>/am/idpsaehandler/metaAlias/idp1" ]]`                      |
  > | `idpAuthncontextMapper`          | `[ "com.sun.identity.saml2.plugins.DefaultIDPAuthnContextMapper" ]`                     |
  > | `cotlist`                        | `[ "cot1" ]`                                                                            |

* Map\<String, List\<String>> getSpAttributes()

  Returns a map containing the extended configuration for the remote SP.

  > **Collapse: Example SP attributes**
  >
  > | SP attribute    | Example value |
  > | --------------- | ------------- |
  > | `metaAlias`     | `[ ]`         |
  > | `treeName`      | `[ "test" ]`  |
  > | `idpProxyCount` | `[ "0" ]`     |
  > | `cotlist`       | `[ "cot1" ]`  |

* Example

  The following script calls the `samlApplication` methods to log the authentication request and configuration values:

  * Next-generation

  * Legacy

  ```javascript
  function logObject(objType, obj) {
    var logMsg = objType + " \n";
      for (attr in obj) {
        logMsg += "\t" + attr + " = " + obj[attr] + "\n";
    }
    logger.info(logMsg);
  }

  if (typeof (samlApplication) === "undefined") {
    logger.error("Journey is not associated with a SAML application");
    action.goTo("false");
  }
  else {
      logger.info("Flow type: " + samlApplication.getFlowInitiator());
      logger.info("SAML app ID: " + getApplicationId());

      if (flowType == 'SP') {
        var authnRequest = samlApplication.getAuthnRequest();
        logObject("authnRequest", authnRequest);
        var assertion = samlApplication.getAssertion();

          if (assertion != null) {
            var issuer = assertion.get("issuer");
            logObject("issuer", issuer.get("value"));
          }
      }

      logObject("idpAttributes", samlApplication.getIdpAttributes());
      logObject("spAttributes", samlApplication.getSpAttributes());

      action.goTo("true");
  }
  ```

  *Not available in Legacy bindings*.

### Existing session properties

The `existingSession` binding is only present when performing a session upgrade. Any properties added by nodes earlier in the journey only appear in the new authenticated session when the journey completes. These properties aren't available in the `existingSession` binding.

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

* `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 isn't a session upgrade and doesn't provide an existing authenticated session, the `existingSession` variable isn't declared. Check for a declaration before attempting to access the variable.

  * Next-generation

  * Legacy

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

  action.goTo("true");
  ```

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

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

  action = fr.Action.goTo("true").build();
  ```

### Customize account lockout message

A script can display a custom error message to the end user when their account is locked or inactive. To do this, use a script with the `.withLockoutMessage` method on the `Action.goto()` object.

The following example changes the account lockout message to a custom error message:

* Next-generation

* Legacy

```javascript
action.goTo("true")
  .withLockoutMessage("Custom lockout message");
```

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

action = fr.Action.goTo("true")
  .withLockoutMessage("Custom lockout message")
  .build();
```

Add the script to a [Scripted Decision node](https://docs.pingidentity.com/auth-node-ref/latest/scripted-decision.html) in your authentication journey.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                     |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | This Scripted Decision node must go after a node that checks the status of the user (such as the [Identity Store Decision node](https://docs.pingidentity.com/auth-node-ref/latest/cloud/identity-store-decision.html) or the [Account Active Decision node](https://docs.pingidentity.com/auth-node-ref/latest/account-active-decision.html)) because it is triggered when a user's account is locked or inactive. |

### Use callbacks

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

The following script checks for a password and stores the username to shared state for use by subsequent nodes in the authentication journey.

 

* Next-generation

* Legacy

Next-generation callback functionality is split between two bindings:

* `callbacksBuilder`: Request callbacks. Callbacks are sent automatically when the script completes.

  > **Collapse: request methods**
  >
  > * `public void textOutputCallback(int messageType, String message)`
  >
  > * `public void suspendedTextOutputCallback(int messageType, String message)`
  >
  > * `public void hiddenValueCallback(String id, String value)`
  >
  > * `public void choiceCallback(String prompt, String[] choices, int defaultChoice, boolean multipleSelectionsAllowed)`
  >
  > * `public void nameCallback(String prompt)`
  >
  > * `public void nameCallback(String prompt, String defaultName)`
  >
  > * `public void passwordCallback(String prompt, boolean echoOn)`
  >
  > * `public void textInputCallback(String prompt)`
  >
  > * `public void textInputCallback(String prompt, String defaultText)`
  >
  > * `public void scriptTextOutputCallback(String message)`
  >
  > * `public void redirectCallback(String redirectUrl, Map redirectData, String method)`
  >
  > * `public void redirectCallback(String redirectUrl, Map redirectData, String method, String statusParameter, String redirectBackUrlCookie)`
  >
  > * `public void metadataCallback(Object outputValue)`
  >
  > * `public void stringAttributeInputCallback(String name, String prompt, String value, Boolean required)`
  >
  > * `public void stringAttributeInputCallback(String name, String prompt, String value, Boolean required, List<String> failedPolicies)`
  >
  > * `public void stringAttributeInputCallback(String name, String prompt, String value, Boolean required, Object policies, Boolean validateOnly)`
  >
  > * `public void stringAttributeInputCallback(String name, String prompt, String value, Boolean required, Object policies, Boolean validateOnly, List<String> failedPolicies)`
  >
  > * `public void numberAttributeInputCallback(String name, String prompt, Double value, Boolean required)`
  >
  > * `public void numberAttributeInputCallback(String name, String prompt, Double value, Boolean required, List<String> failedPolicies)`
  >
  > * `public void numberAttributeInputCallback(String name, String prompt, Double value, Boolean required, Object policies, Boolean validateOnly)`
  >
  > * `public void numberAttributeInputCallback(String name, String prompt, Double value, Boolean required, Object policies, Boolean validateOnly, List<String> failedPolicies)`
  >
  > * `public void booleanAttributeInputCallback(String name, String prompt, Boolean value, Boolean required)`
  >
  > * `public void booleanAttributeInputCallback(String name, String prompt, Boolean value, Boolean required, List<String> failedPolicies)`
  >
  > * `public void booleanAttributeInputCallback(String name, String prompt, Boolean value, Boolean required, Object policies, Boolean validateOnly)`
  >
  > * `public void booleanAttributeInputCallback(String name, String prompt, Boolean value, Boolean required, Object policies, Boolean validateOnly, List<String> failedPolicies)`
  >
  > * `public void confirmationCallback(int messageType, int optionType, int defaultOption)`
  >
  > * `public void confirmationCallback(int messageType, String[] options, int defaultOption)`
  >
  > * `public void confirmationCallback(String prompt, int messageType, int optionType, int defaultOption)`
  >
  > * `public void confirmationCallback(String prompt, int messageType, String[] options, int defaultOption)`
  >
  > * `public void languageCallback(String language, String country)`
  >
  > * `public void idPCallback(String provider, String clientId, String redirectUri, List<String> scope, String nonce, String request, String requestUri, List<String> acrValues, boolean requestNativeAppForUserInfo)`
  >
  > * `public void idPCallback(String provider, String clientId, String redirectUri, List<String> scope, String nonce, String request, String requestUri, List<String> acrValues, boolean requestNativeAppForUserInfo, String token, String tokenType)`
  >
  > * `public void pollingWaitCallback(String waitTime, String message)`
  >
  > * `public void validatedPasswordCallback(String prompt, boolean echoOn, Object policies, Boolean validateOnly)`
  >
  > * `public void validatedPasswordCallback(String prompt, boolean echoOn, Object policies, Boolean validateOnly, List<String> failedPolicies)`
  >
  > * `public void validatedUsernameCallback(String prompt, Object policies, Boolean validateOnly)`
  >
  > * `public void validatedUsernameCallback(String prompt, Object policies, Boolean validateOnly, List<String> failedPolicies)`
  >
  > * `public void httpCallback(String authorizationHeader, String negotiationHeader, String errorCode)`
  >
  > * `public void httpCallback(String authRHeader, String negoName, String negoValue, int errorCode)`
  >
  > * `public void x509CertificateCallback(String prompt)`
  >
  > * `public void x509CertificateCallback(String prompt, X509Certificate certificate)`
  >
  > * `public void x509CertificateCallback(String prompt, X509Certificate certificate, boolean requestSignature)`
  >
  > * `public void consentMappingCallback(Object config, String message, Boolean isRequired)`
  >
  > * `public void consentMappingCallback(String name, String displayName, String icon, String accessLevel, List<String> titles, String message, Boolean isRequired)`
  >
  > * `public void deviceProfileCallback(Boolean metadata, Boolean location, String message)`
  >
  > * `public void kbaCreateCallback(String prompt, List<String> predefinedQuestions, boolean allowUserDefinedQuestions)`
  >
  > * `public void selectIdPCallback(Object providers)`
  >
  > * `public void termsAndConditionsCallback(String version, String terms, String createDate)`

* `callbacks`: Use `isEmpty()` to check if callbacks have returned to Advanced Identity Cloud. Use public `get` methods to retrieve input from [interactive](../am-authentication/callbacks-interactive.html) and [backchannel callbacks](../am-authentication/callbacks-backchannel.html).

  > **Collapse: public  methods**
  >
  > | Method                                | Return type                 | Description                                                                                                                                                                                                                                                                                                                                                                     |
  > | ------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  > | `getBooleanAttributeInputCallbacks()` | `List<Boolean>`             | The collected Boolean value                                                                                                                                                                                                                                                                                                                                                     |
  > | `getChoiceCallbacks()`                | `List<int[]>`               | The selected choices as indexes                                                                                                                                                                                                                                                                                                                                                 |
  > | `getConfirmationCallbacks()`          | `List<Integer>`             | The selected confirmation option                                                                                                                                                                                                                                                                                                                                                |
  > | `getConsentMappingCallbacks()`        | `List<Boolean>`             | The collected Boolean value                                                                                                                                                                                                                                                                                                                                                     |
  > | `getDeviceProfileCallbacks()`         | `List<String>`              | The device information in escaped JSON format                                                                                                                                                                                                                                                                                                                                   |
  > | `getHiddenValueCallbacks()`           | `Map<String, String>`       | The map of hiddenValueCallbacks                                                                                                                                                                                                                                                                                                                                                 |
  > | `getHttpCallbacks()`                  | `List<String>`              | The authorization token                                                                                                                                                                                                                                                                                                                                                         |
  > | `getIdpCallbacks()`                   | `List<Map<String, Object>>` | A map of IDP callback data. Values are:- `nodeName` (String)
  >
  > - `provider` (String)
  >
  > - `clientId` (String)
  >
  > - `redirectUri` (String)
  >
  > - `scope` (List\<String>)
  >
  > - `nonce` (String)
  >
  > - `request` (String)
  >
  > - `requestUri` (String)
  >
  > - `acrValues` (List\<String>)
  >
  > - `token` (String)
  >
  > - `tokenType` (String)
  >
  > - `userInfo` (String)
  >
  > - `requestNativeAppForUserInfo` (boolean) |
  > | `getKbaCreateCallbacks()`             | `List<Map<String, String>>` | A map of knowledge-based authentication (KBA) responses. Values are `selectedQuestion` and `selectedAnswer`.                                                                                                                                                                                                                                                                    |
  > | `getLanguageCallbacks()`              | `List<String>`              | The selected locale                                                                                                                                                                                                                                                                                                                                                             |
  > | `getNameCallbacks()`                  | `List<String>`              | The collected name value                                                                                                                                                                                                                                                                                                                                                        |
  > | `getNumberAttributeInputCallbacks()`  | `List<Double>`              | The collected numeric value                                                                                                                                                                                                                                                                                                                                                     |
  > | `getPasswordCallbacks()`              | `List<String>`              | The collected password value                                                                                                                                                                                                                                                                                                                                                    |
  > | `getSelectIdPCallbacks()`             | `List<Map<String, Object>>` | A map of selected IDP providers in JSON format                                                                                                                                                                                                                                                                                                                                  |
  > | `getStringAttributeInputCallbacks()`  | `List<String>`              | The collected String value                                                                                                                                                                                                                                                                                                                                                      |
  > | `getTermsAndConditionsCallbacks()`    | `List<Boolean>`             | The Boolean acceptance value                                                                                                                                                                                                                                                                                                                                                    |
  > | `getTextInputCallbacks()`             | `List<String>`              | The collected text as a String value (can be null)                                                                                                                                                                                                                                                                                                                              |
  > | `getValidatedPasswordCallbacks()`     | `List<Map<String, Object>>` | A map with the password returned as `value` (String) and `validateOnly`(whether to only validate input, or validate and continue journey)                                                                                                                                                                                                                                       |
  > | `getValidatedUsernameCallbacks()`     | `List<Map<String, Object>>` | A map with the username returned as `value` (String) and `validateOnly`(whether to only validate input, or validate and continue journey)                                                                                                                                                                                                                                       |
  > | `getX509CertificateCallbacks()`       | `List<Map<String, Object>>` | A map of X.509 certificate callback data. Values are:- `certificate` (X509Certificate)
  >
  > - `signature` (String)
  >
  > - `reqSignature` (Boolean)                                                                                                                                                                                                                                      |

The following examples show how to use the `callbacksBuilder` and `callbacks` objects to request and receive input from callbacks:

> **Collapse: Example nameCallback and passwordCallback**
>
> ```javascript
> if (callbacks.isEmpty()) {
>   // Request callbacks
>   callbacksBuilder.nameCallback("User Name", "User Name");
>   callbacksBuilder.passwordCallback("Password", false);
> } else {
>   // Callbacks returned from browser, save username and password
>   var username = callbacks.getNameCallbacks().get(0);
>   var password = callbacks.getPasswordCallbacks().get(0);
>
>   nodeState.putShared("username", username);
>
>   if (password === null || !password) {
>     action.goTo("false");
>   } else {
>     nodeState.putTransient("password", password);
>     action.goTo("true");
>   }
> }
> ```

> **Collapse: Example metadataCallback and textOutputCallback**
>
> ```javascript
> var data = {
>       mfaType: "email"
>     };
>
>
> // Get UUID by enabling `Username as Universal Identifier`
> // in a preceding Identity Store Decision node
> var uuid = nodeState.get("username");
> var identity = idRepository.getIdentity(uuid);
>
> var firstName = identity.getAttributeValues('givenName')[0];
>
> if (callbacks.isEmpty()) {
>   // Adds the key-value pair to the JSON response
>   callbacksBuilder.metadataCallback(data);
>
>   // Displays a message to the end user
>   // Message type: INFORMATION (0), WARNING(1), or ERROR (2)
>   callbacksBuilder.textOutputCallback(1, "Hi " + firstName + ", please update your postal code");
> }
> action.goTo("true");
> ```

> **Collapse: Example choiceCallback**
>
> ```javascript
> var titles = ["Mr", "Mrs", "Ms", "Mx", "Other"];
>
> if (callbacks.isEmpty()) {
>   // Request choice from the user
>     callbacksBuilder.choiceCallback("Select a title", titles, 0, false);
>
> } else {
>   // Callbacks returned from browser, save selected choice
>   var index = callbacks.getChoiceCallbacks().get(0)[0];
>   var title = titles[index];
>
>   if (title === null || !title) {
>     action.goTo("false");
>   } else {
>     nodeState.putShared("title", title);
>     action.goTo("true");
>   }
> }
> ```

> **Collapse: Example hiddenValueCallback and scriptTextOutputCallback**
>
> ```javascript
> var script = "document.getElementById('clientScriptOutputData').value = navigator.language \n" +
>     "document.getElementById('loginButton_0').click()";
> try {
>     if (callbacks.isEmpty()) {
>         callbacksBuilder.hiddenValueCallback("clientScriptOutputData", "false");
>         callbacksBuilder.scriptTextOutputCallback(script);
>
>     } else {
>         //Browser language can be used to localize custom callback messages
>         var lang = callbacks.getHiddenValueCallbacks().get("clientScriptOutputData");
>         nodeState.putShared("preferredLanguage", lang);
>         action.goTo("true");
>     }
>     action.goTo("true");
>
> } catch (error) {
>     action.goTo("false");
>     logger.error("Error getting browser language: " + error.toString());
> }
> ```

```javascript
var fr = JavaImporter(
  org.forgerock.openam.auth.node.api.Action,
  javax.security.auth.callback.NameCallback,
  javax.security.auth.callback.PasswordCallback,
  java.lang.String
);

if (callbacks.isEmpty()) {
  // Request callbacks
  action = fr.Action.send(
    new fr.NameCallback("User Name"),
    new fr.PasswordCallback("Password", false)).build();
} else {
  // Callbacks returned with credentials
  var username = fr.String(callbacks.get(0).getName());
  var password = fr.String(callbacks.get(1).getPassword());

  sharedState.put("username", username);
  if (password === null || !password) {
    action = fr.Action.goTo("false").build();
  } else {
    transientState.put("password", password);
    action = fr.Action.goTo("true").build();
  }
}
```

You can find a list of supported callbacks in [Return callback information](../am-authentication/callbacks-supported.html).

### Suspend and resume journeys

You can use the `action` object to suspend an authentication journey and send a message to display to the user. Evaluate the `resumedFromSuspend` script binding to determine whether to continue the journey.

* `ActionWrapper suspend(String message)`

  Suspend the current journey session and send a message to display to the user.

* `ActionWrapper suspend(String message, SuspensionLogic logic)`

  Suspend the current journey session and send a message to display to the user. This method also accepts a callable function that references the resume URI at the point of suspension.

  * Next-generation

  * Legacy

  ```javascript
  // Use the resumedFromSuspend object to check if the journey has resumed after suspension
  // For example, it returns true after sending an email and receiving a response
  if (resumedFromSuspend) {
    action.goTo("true");
  } else {
    // Send a message to display to the user and the URL to resume the journey
    action.suspend('Check your email for a magic sign-in link.', (resumeUri) => {
      requestOptions = {
        "method": "POST",
        "body": {
          "resumeUri": resumeUri
        }
      }
      // Use internal mail server to send email
      httpClient.send("https://my.email.service/sendEmail", requestOptions)
    });
  }
  ```

  *Not supported for Legacy bindings*.

* `ActionWrapper suspend(String message, SuspensionLogic logic, int maximumSuspendDuration)`

  Suspend the current journey session, send a message to display to the user, and set the suspend duration.

  |   |                                                                                                                                                           |
  | - | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | When the suspend duration is set, it overrides the [suspend duration](../am-authentication/suspended-auth.html#suspended-auth-duration) set in the realm. |

  * Next-generation

  * Legacy

  ```javascript
  if (resumedFromSuspend) {
    action.goTo("true");
  } else {
  // Send a message to display to the user and set the suspend duration to `1` minute
    action.suspend("Tree is suspended. Click link to proceed: [{0}]",
       (_) => {}, 1);
    }
  ```

  *Not supported for Legacy bindings*.

### Add information to authentication audit log entries

A script can add information to audit log entries with the `auditEntryDetail` variable. Advanced Identity Cloud appends the value of the variable to the authentication audit logs, which corresponds to the `am-authentication` log source.

The `auditEntryDetail` variable can hold either a string or a JSON object:

* To add a string to authentication audit log entries:

  ```javascript
  auditEntryDetail = "Additional audit information"
  ```

* To add a JSON object to authentication audit log entries:

  ```javascript
  auditEntryDetail = {
      "transactionStatus": "Success"
  }
  ```

  |   |                                                                                                                                                                                                                                                                                                                                                                 |
  | - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  |   | Ping Identity recommends that you don't put spaces in the keys of JSON objects as it prevents you from using a [query filter](../tenants/audit-debug-logs.html#filter-log-results) to search additional audit information. For example, the following JSON would not be searchable:```javascript
  auditEntryDetail = {
      "Transaction Status": "Success"
  }
  ``` |

Advanced Identity Cloud adds the value of `auditEntryDetail` to the `payload > entries > info > nodeExtraLogging > auditInfo` field of the audit log entry:

```json
{
  "payload": {
    "_id": "de5abe95-db97-4354-9d32-aab660ea23a3-4392064",
    "component": "Authentication",
    "entries": [{
      "info": {
        "authLevel": "0",
        "displayName": "Scripted Decision",
        "nodeExtraLogging": {
          "auditInfo": {
            "transactionStatus": "Success"
          }
        },
        "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"
}
```
