---
title: Scripted decision node API
description: A Scripted Decision node calls server-side Groovy or 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: pingam
version: 7.4
page_id: pingam:am-scripting:scripting-api-node
canonical_url: https://docs.pingidentity.com/pingam/8.1/am-scripting/scripting-api-node.html
llms_txt: https://docs.pingidentity.com/pingam/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
keywords: ["Authentication", "Sessions", "Nodes &amp; Trees", "Scripting"]
page_aliases: ["authentication-guide:scripting-api-node.adoc", "scripting-guide:scripting-api-node.adoc"]
superseded_by: https://docs.pingidentity.com/pingam/8.1/am-scripting/scripting-api-node.html
section_ids:
  scripted-decision-node-bindings: Script bindings
  action-set-outcome: Set script outcome and script attributes
  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
  scripting-api-node-sessionProperties: Set session properties
  scripting-api-node-existingSession: Existing session properties
  jwt-support: Generate and validate JWTs
  scripting-api-node-callbacks: Use callbacks
  scripting-api-node-openidm: Access IDM scripting functions
  scripting-api-authn-secrets: Accessing credentials and secrets
  methods: Methods
  scripting-api-node-audit-logging: Audit information
---

# Scripted decision node API

A [Scripted Decision node](https://docs.pingidentity.com/auth-node-ref/7.4/scripted-decision.html) calls server-side Groovy or JavaScript to set the outcome for the node programmatically and determine the path the authentication journey takes. The script can perform actions before setting the outcome.

To create and manage decision scripts, refer to [Manage scripts (UI)](manage-scripts-console.html). Use the script type Decision node script for authentication trees.

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

The outcome strings aren't limited to `true` and `false`. Specify a value for each possible outcome and as many outcomes as required by the tree.

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

## Script bindings

In addition to the [common bindings](script-bindings.html#common-bindings), a decision node script has access to the following specific *bindings*, predefined objects that PingAM injects into the script execution context.

|   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | PingAM has introduced a [next-generation scripting engine](next-generation-scripts.html) that offers several benefits, including enhanced script bindings.For decision node scripts, 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.The next-generation script engine is *only* available to the [Scripted decision node API](scripting-api-node.html).For information about migrating to the enhanced scripting engine, refer to [Migrating 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) |
| `auditEntryDetail`                | Add information to the PingAM audit logs.                                                                              | [Audit information](#scripting-api-node-audit-logging)                                                                         |
| `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 a session, use this binding to access the properties of that session. | [Existing session properties](#scripting-api-node-existingSession)                                                             |
| `httpClient`                      | Make outbound HTTP calls.                                                                                              | [Access HTTP services](script-bindings.html#httpclient)                                                                        |
| `idRepository`                    | Access the data stored in the user's profile.                                                                          | [Access profile data](#scripting-api-node-id-repo)                                                                             |
| `jwtAssertion`(1)                 | Generate JWT assertions in scripts.                                                                                    | [Generate and validate JWTs](#jwt-support)                                                                                     |
| `jwtValidator`(1)                 | Validate JWT assertions in scripts.                                                                                    | [Generate and validate JWTs](#jwt-support)                                                                                     |
| `logger`                          | Write a message to the PingAM debug log.                                                                               | [Log script messages](script-bindings.html#logger)                                                                             |
| `realm`                           | Return the name of the realm to which the user is authenticating as a string.                                          | For example, authenticating to the `alpha` realm returns a string value of `/alpha`.                                           |
| `requestHeaders`                  | Access the HTTP headers provided in the login request.                                                                 | [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.                                                   | For example, the value returns `True` after sending an email and receiving the response.                                       |
| `secrets`                         | Access the secrets configured for PingAM in the environment.                                                           | [ScriptedSecrets](../_attachments/apidocs/org/forgerock/openam/scripting/api/secrets/ScriptedSecrets.html)                     |
| `nodeState`                       | Access data set by previous nodes in the tree, or store data to be used by subsequent nodes.                           | [Access shared state data](#scripting-api-node-nodeState)                                                                      |
| `openidm`(1)                      | Access scripted functions in IDM to manage resources.                                                                  | [Access IDM scripting functions](#scripting-api-node-openidm)                                                                  |
| `scriptName`                      | Return the name of the running script.                                                                                 | [Output script name](script-bindings.html#scriptName).                                                                         |
| `systemEnv`                       | Lets you get the value of an environment secret or variable (ESV).                                                     | [Reference ESVs in scripts](script-bindings.html#scripting-esvs).                                                              |

(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 the `outcome` variable 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.Refer to [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 the action.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
> | `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/7.4/failure.html).                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
> | `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. If the advanced server property, `org.forgerock.am.auth.trees.authenticate.identified.identity` is set to true, AM uses the stored identified identities to decide which user to log in.This lets the authentication tree engine correctly resolve identities that have the same username.For more information, refer to [advanced server properties](../reference/deployment-configuration-reference.html#org.forgerock.am.auth.trees.authenticate.identified.identity). |
> | `public ActionWrapper withLockoutMessage(String message)`                                                              | Set an error message to display to the end user when the account is locked or inactive.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

```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.Refer to [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 the 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/7.4/failure.html).                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
> | `public ActionBuilder withIdentifiedIdentity(AMIdentity id)``public ActionBuilder withIdentifiedIdentity(String username, IdType id)` | Set the identity, authenticated or not, of the user or agent verified to exist in an identity store.Use these methods to record the type of identified user. If the advanced server property, `org.forgerock.am.auth.trees.authenticate.identified.identity` is set to true, AM uses the stored identified identities to decide who to log in.This lets the authentication tree engine correctly resolve identities that have the same username.For more information, refer to [advanced server properties](../reference/deployment-configuration-reference.html#org.forgerock.am.auth.trees.authenticate.identified.identity). |
> | `public ActionBuilder withLockoutMessage(String message)`                                                                             | Set an error message to display to the end user when the account is locked or inactive.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
> | `public ActionBuilder withStage(String stage)`                                                                                        | Set a stage name to return to the client to aid the rendering of the UI. The property is only sent if the script also sends callbacks.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |

For more information, refer to [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 = 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).

### 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 isn't 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 tree 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();
  ```

### Access profile data

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

* Get attribute values

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

  * Next-generation

  * Legacy

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

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

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

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

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

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

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

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

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

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

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

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

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

* Set attribute values

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

  * Next-generation

  * Legacy

  ```javascript
  var username = nodeState.get("username");

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

  // 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");
  }
  ```

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

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

  var username = nodeState.get("username").asString();

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

  try {
    // check setAttribute was successful
    var mail = idRepository.getAttribute(username, "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
  var username = nodeState.get("username");

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

  // 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");
  }
  ```

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

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

  var username = nodeState.get("username").asString();
  var attr = "mail";

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

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

  // check addAttribute was successful
  var updatedmail = idRepository.getAttribute(username, 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();
  }
  ```

### 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/7.4/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](../reference/global-services-configuration.html#global-amsessionpropertywhitelist), it appears in the `properties` object of the JSON response. For example:

```json
{
  "username": "bjensen",
  "universalId": "id=bjensen,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"
  }
}
```

### 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 on the user's new session when the journey completes; such properties are not 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 is not a session upgrade and does not provide an existing session, the `existingSession` variable is not declared. Check for a declaration before attempting to access the variable.

  ```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();
  ```

### Generate and validate JWTs

Use the `jwtAssertion` and `jwtValidator` bindings to include JWT operations in your script.

The `jwtAssertion` binding lets you generate a signed and/or encrypted JWT, for example, for securely transporting JWT claims that can be verified later in the journey via an emailed magic link. JWTs are signed using the HS256 or RS256 algorithms.

The `jwtValidator` binding lets you decrypt and validate JWTs; for example, to verify JWT claims from another system such as Identity Gateway.

* `String jwtAssertion.generateJwt(Map<String, Object> jwtData)`

  Provide the following data for generating a JWT assertion:

  | Data field        | Description                                                                                                                                                                                                                                                       |
  | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `jwtType`         | (*Required*) The type of JWT to generate: `SIGNED`, `SIGNED_THEN_ENCRYPTED`, or `ENCRYPTED_THEN_SIGNED`.Encrypted options are only supported for HS256 JWTs.                                                                                                      |
  | `jwsAlgorithm`    | (*Required*) `HS256` or `RS256` (`jwtType` must be `SIGNED`).                                                                                                                                                                                                     |
  | `issuer`          | The JWT issuer (`iss`) claim. You must provide a value for either `issuer` or `stableId` at a minimum.                                                                                                                                                            |
  | `audience`        | The JWT audience (`aud`) claim.                                                                                                                                                                                                                                   |
  | `subject`         | The JWT subject (`sub`) claim.                                                                                                                                                                                                                                    |
  | `type`            | The JWT type (`typ`) claim, typically `JWT`.                                                                                                                                                                                                                      |
  | `jwtId`           | Specify a value for the JWT ID (`jti`).                                                                                                                                                                                                                           |
  | `claims`          | Custom claims in the format `"key":"value"`.                                                                                                                                                                                                                      |
  | `stableId`        | Stable ID. You must provide a value for either `issuer` or `stableId` at a minimum.                                                                                                                                                                               |
  | `accountId`       | *Deprecated*. To support backwards compatibility, the account ID (if specified) is set as the `issuer`, `stableId` and `subject` when these values aren't provided.                                                                                               |
  | `validityMinutes` | The duration of the validity of the JWT in minutes.                                                                                                                                                                                                               |
  | `privateKey`      | The RSA private key in JSON Web Key (JWK) [format](https://www.rfc-editor.org/rfc/rfc7518#section-6.3). For example, generate your own private key with <https://www.openssl.org>.Required for `RS256`-signed JWTs.                                               |
  | `signingKey`      | The base64-encoded [JSON Web Key](https://www.rfc-editor.org/rfc/rfc7517.html) used to sign/verify the JWT.Required for `HS256`-signed JWTS.                                                                                                                      |
  | `encryptionKey`   | The [JSON Web Encryption (JWE)](https://www.rfc-editor.org/rfc/rfc7516.html) public key used to encrypt/decrypt the JWT.JWTs are encrypted using the A128CBC-HS256 algorithm.*Generation of **RS256-encrypted** signed JWT assertions isn't currently supported*. |

  * Next-generation

  * Legacy

  **JWT generation example 1: HS256 encrypted then signed:**

  ```javascript
  var hmacJwtData = {
    jwtType: "ENCRYPTED_THEN_SIGNED",
    jwsAlgorithm: "HS256",
    issuer: "issuer",
    subject: "subject",
    audience: "https://am.localtest.me:8080/am/oauth2/access_token",
    type: "JWT",
    validityMinutes: 10,
    claims: {
      "key1": "value1",
      "key2": "value2"
    },
    signingKey: "cGFzc3dvcmQ=",
    encryptionKey: "Syz1K5XQCZtq7FkE+GNvgZPeFyvUXJdemIW7CQjM18U="
  };
  // returns a string representation of the JWT
  var assertionJwt = jwtAssertion.generateJwt(hmacJwtData);

  if (assertionJwt !== null && assertionJwt.length > 0) {
    logger.debug("AssertionJwt: " + assertionJwt);
    // store JWT in nodeState for validation later in journey
    nodeState.putShared("assertionJwt" , assertionJwt);
    action.goTo("true");
  } else {
    action.goTo("false");
  }
  ```

  **JWT generation example 2: RS256 signed:**

  ```javascript
  var rsaJwtData = {
    jwtType: "SIGNED",
    jwsAlgorithm: "RS256",
    accountId: "accountId",
    audience: "https://am.localtest.me:8080/am/oauth2/access_token",
    validityMinutes: 10,
    privateKey: {
      "p": "0BRJ6TrTpeT3XM1aXj9ZiTDfVTD0Aqufhm0a2Cm7Zr3ObqkBbZrm2KH9BY23nfY_TFbd6kx31YHqjpoV4KeThn2uvZ7gPw_ILljk5WQwgFq_gdDvAq3Iw4MlwGoR51nSaGGqKU6Dt2PvVOB4I3azVJr1f9Vsm47L6Llp8YjKtBE",
      "kty": "RSA",
      "q": "ugtiWmODuy3XuWGf3u5hvF4RglZZK5IPYRkJjSz8j90DxYVPD_CBNJx8j_FGUJ6BBNRTs2yylgMJdcFs-WxcFR7iQD2OzgzSdeRgrh34RmLjAXEq-X0OF9P3lqnBXIx-uVL3-rG8jQWSzc016o3PWjclOKqLx7oBmYs7w6WbJLE",
      "d": "c2DR3SOQkzu6f5eRLFLURphQCbrN4JCAlOo2S_D07UJmMiYtIFpOezbLazQYXdebiV-pPv-zZcOWHhOr3HMgYLu8JBN29mrJS8kDIWMjCx8vMJgrNLfBZO4az--t_Kyow4p0n3HdaYRu0K4lqskXe68Syl0XAlHfnd7q-bB-UUGB89j3E-FfXIIjKktn_koBc8hX2DhUnJgduFi9CcAQVO9wSjjfyB6ksn01_YaMt5MHsHKhqDvZeCdEHfwyFer8vvDRCTru_msl_fu_MqQi15igTJu6f7eBsVQQLnb7L1fQ0BURck7oVMjTvyYz_nRbnMkKSrWpa-j1d1Z4TTucAQ",
      "e": "AQAB",
      "use": "sig",
      "kid": "iUE-em7bU5j1yJN6hEMKx6ZmtWBYGZycCuO94X1sssE",
      "qi": "rUqLFjJ2L3FNrj65tdUCusQ-_7g1rKsTOGnQUrVcgYSsHb3aYR90zV651MiL0X4gp6mzgc8QcSdzc1KbEmR5VHm5IH4N6f9yBNb2yO_8sftmS8PiRjZjVLUORnwmouJ4cPsob0RPx9mwGLIURLxQDstE4UQ0j6iMWF74iezwxO8",
      "dp": "f60DURXkijV9RrdGjPAIK3MOhoJ8JytRvjUyNJMex0MN6L7Q_oT-wsxaqc60bTuMyXW_wyVanmqSFyAa7ndEwVBbKUTUSj2P0kh_YvXgANIuEiS2k4k42CafwnoTNEbcIWpT8_aWQbATSZxWe0Q5c1-F5gN6GdU77zfd9vO9lVE",
      "alg": "RS256",
      "dq": "A8RdPnVLYovgFVnbsdjj07uX4Sq8bXxsoUuvfNNPXd5cyDAV1L3K7_THNObuxI0hEab29ugZiZ4QH_lFqps-FhNlA2X7sUJjNI3mQ0BKGarA6_ONqjWVBnh9R-iyCJyzqC785G-a4MQfH9mq4M_0ReBd-ZLCd83VYHWIRULiLFE",
      "n": "lzf-g94TfT9JeQ7kRZCkSOKwP0rtpe2IzX6C42NN1EE4hejwCjIAj4oUHrcNyrcVZ6hIu3a5r09fnzp5w9RS6ooMkp-AQ_9C5T8hFbf61J4lT3kmIp-jv50MF7oJCGOidLPCiJ-3EsnBKFjomRcCQa_r-sHF9hfORvhypBcj_U1OMMRM28yX7JVggG7f_YPlmx03c7meKPE-4xXnfdFDxbjbntlWSul6qm0YOTRVfuZHubwu-HOZEDB4dKkA3-0qAev6FULW7tNOqoMj5KLlejeiuCOQeAMTqPnBqOdH8iC4u6WHArsRqkmOdLgVs0uLlVPz9796qIKlosP5EMPjwQ"
    }
  };

  var assertionJwt = jwtAssertion.generateJwt(rsaJwtData);

  if (assertionJwt !== null && assertionJwt.length > 0) {
    logger.debug("AssertionJwt: " + assertionJwt);
    // store JWT in nodeState for validation later in journey
    nodeState.putShared("assertionJwt" , assertionJwt);
    action.goTo("true");
  } else {
    action.goTo("false");
  }
  ```

  *Not available in Legacy bindings*

* `Map<String, Object> jwtValidator.validateJwtClaims(Map<String, Object> jwtData)`

  Provide values for the following supported fields to validate the specified JWT assertion:

  | Data field      | Description                                                                                                                                                                                                                                                       |
  | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `jwtType`       | (*Required*) The type of JWT to validate: `SIGNED`, `SIGNED_THEN_ENCRYPTED`, or `ENCRYPTED_THEN_SIGNED`.                                                                                                                                                          |
  | `jwt`           | (*Required*) The JWT object to validate.                                                                                                                                                                                                                          |
  | `issuer`        | The JWT issuer (`iss`) claim. You must provide a value for either `issuer` or `stableId` at a minimum.                                                                                                                                                            |
  | `audience`      | The JWT audience (`aud`) claim value.                                                                                                                                                                                                                             |
  | `subject`       | The JWT subject (`sub`) claim.                                                                                                                                                                                                                                    |
  | `type`          | The JWT type (`typ`) claim.                                                                                                                                                                                                                                       |
  | `claims`        | Custom claims in the format `"key":"value"`.                                                                                                                                                                                                                      |
  | `stableId`      | Stable ID. You must provide a value for either `issuer` or `stableId` at a minimum.                                                                                                                                                                               |
  | `accountId`     | *Deprecated*. To support backwards compatibility, the account ID (if specified) is used to validate the `issuer`, `stableId` and `subject` when these values aren't provided.                                                                                     |
  | `signingKey`    | The base64-encoded [JSON Web Key](https://www.rfc-editor.org/rfc/rfc7517.html) used to sign/verify the JWT.JWTS are signed using the HS256 or RS256 algorithm.                                                                                                    |
  | `encryptionKey` | The [JSON Web Encryption (JWE)](https://www.rfc-editor.org/rfc/rfc7516.html) public key used to encrypt/decrypt the JWT.JWTs are encrypted using the A128CBC-HS256 algorithm.*Validation of **RS256-encrypted** signed JWT assertions isn't currently supported*. |

  * Next-generation

  * Legacy

  **JWT validation example 1: HS256 encrypted then signed:**

  ```javascript
  var jwtData = {
    jwtType: "ENCRYPTED_THEN_SIGNED",
    issuer: "issuer",
    subject: "subject",
    audience: "https://am.localtest.me:8080/am/oauth2/access_token",
    type: "JWT",
    signingKey: "cGFzc3dvcmQ=",
    encryptionKey: "Syz1K5XQCZtq7FkE+GNvgZPeFyvUXJdemIW7CQjM18U="
  };
  // get JWT from nodeState (JWT generation example 1)
  var assertionJwt = nodeState.get("assertionJwt");

  if (assertionJwt !== null && assertionJwt.length > 0) {
    jwtData["jwt"] = assertionJwt;

    try {
      // returns a map of JWT claims or null if required claims are missing
      // throws NoSuchSecretException if verification key is missing
      var jwtClaims = jwtValidator.validateJwtClaims(jwtData);
      if (jwtClaims !== null) {
        // retrieve and log some JWT claim values
        logger.debug("Audience: " + jwtClaims.get("audience"));
        logger.debug("Subject: " + jwtClaims.get("subject"));
        logger.debug("Expiration Time: " + jwtClaims.get("expirationTime"));
        logger.debug("Issued At: " + jwtClaims.get("issuedAt"));
        logger.debug("JWT ID: " + jwtClaims.get("jwtId"));
        logger.debug("key1: " + jwtClaims.get("key1")); // custom claim

        action.goTo("true");
      } else {
        logger.error("Invalid JWT claims");
        action.goTo("false");
      }
    } catch(e) {
      logger.error("Invalid JWT signing key");
      action.goTo("false");
    }
  } else {
    logger.error("Error getting assertionJwt");
    action.goTo("false");
  }
  ```

  **JWT validation example 2: RS256 signed:**

  ```javascript
  var jwtData = {
    jwtType: "SIGNED",
    accountId: "accountId",
    audience: "https://am.localtest.me:8080/am/oauth2/access_token",
    signingKey: "cGFzc3dvcmQ="
  };
  // get JWT from nodeState (JWT generation example 2)
  var assertionJwt = nodeState.get("assertionJwt");

  if (assertionJwt !== null && assertionJwt.length > 0) {
    jwtData["jwt"] = assertionJwt;

    try {
      // returns a map of JWT claims or null if required claims are missing
      // throws NoSuchSecretException if verification key is missing
      var jwtClaims = jwtValidator.validateJwtClaims(jwtData);
      if (jwtClaims !== null) {
        // retrieve and log JWT claim values
        logger.debug("Audience: " + jwtClaims.get("audience"));
        action.goTo("true");
      } else {
        logger.error("Invalid JWT claims");
        action.goTo("false");
      }
    } catch(e) {
      logger.error("Invalid JWT signing key");
      action.goTo("false");
    }
  } else {
    logger.error("Error getting assertionJwt");
    action.goTo("false");
  }
  ```

  *Not available in Legacy bindings*

### 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 PingAM. Use public `get` methods to retrieve input from [interactive](../am-authentication/authn-interactive-callbacks.html) and [backchannel callbacks](../am-authentication/authn-backchannel-callbacks.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"
>     };
>
> var username = nodeState.get('username');
> var identity = idRepository.getIdentity(username);
>
> 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();
  }
}
```

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

### Access IDM scripting functions

The `openidm` binding lets you manage an IDM resource by calling scripting functions directly from a decision node script.

|   |                                                                             |
| - | --------------------------------------------------------------------------- |
|   | The `openidm` binding is only available when PingAM is integrated with IDM. |

The following CRUDPAQ functions are supported:

* create

* read

* update

* delete

* patch

* action

* query

For more information, refer to [Scripting functions](https://docs.pingidentity.com/pingidm/7.4/scripting-guide/scripting-func-ref.html).

|   |                                                                                                                               |
| - | ----------------------------------------------------------------------------------------------------------------------------- |
|   | The `openidm` provides administrative access to IDM functions. Use it with caution to prevent the exposure of sensitive data. |

The following example illustrates how to create a user, update their details, send an email, and finally delete the user:

* Next-generation

* Legacy

```javascript
// create
var newUser = openidm.create("managed/user", null, {
  "userName": "testUser",
  "mail": "foo@bar.com",
  "givenName": "Foo",
  "sn": "Bar"});

var userID = newUser._id;

// read - returns entire json object
var user = openidm.read("managed/user/" + userID);

// update - replace object
user.description = 'New description';
var updatedUser = openidm.update("managed/user/" + userID, null, user);

// patch - selectively modify object
var patchedUser = openidm.patch("managed/user/" + userID, null, [{
        "operation":"replace",
        "field":"/mail",
        "value":"new@example.com"
}]);

// send email
var actionResponse = openidm.action("external/email", "send", {
    "from": "admin@example.com",
    "to": patchedUser.mail,
    "subject": "Example email",
    "body": "This is an example"
});

// delete
openidm.delete('managed/user/'+ userID, null);

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

*Not available in Legacy bindings*

## Accessing credentials and secrets

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

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

### Methods

* `String secrets.getGenericSecret(String secretId)`

  Returns the value of the specified secret ID.

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

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

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

  Use the following functions to format the returned secret value:

  * `getAsBytes()`

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

  * `getAsUtf8()`

    Retrieve the secret value in UTF-8 format.

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

  * JavaScript

  * Groovy

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

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

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

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

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

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

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

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

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

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

  - `java.util.Base64`

  - `java.util.Base64$Encoder`For details, refer to [Scripting environment](scripting-env.html). |

### Audit information

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

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

The following example shows how to add a string to the authentication audit log entry:

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

PingAM records it in 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": "Additional audit information"
        },
        "nodeId": "d3a9a765-f5d7-41dd-b936-f862c8b672a2",
        "nodeOutcome": "true",
        "nodeType": "ScriptedDecisionNode",
        "treeName": "Test"
      }
    }],
    "eventName": "AM-NODE-LOGIN-COMPLETED",
    "level": "INFO",
    "principal": [
      "014c54bd-6078-4639-8316-8ce0e7746fa4"
    ],
    "realm": "/alpha",
    "source": "audit",
    "timestamp": "2022-10-31T13:06:38.195Z",
    "topic": "authentication",
    "trackingIds": [
      "de5abe95-db97-4354-9d32-aab660ea23a3-4391910"
    ],
    "transactionId": "bd52ff36-8b34-4819-8e50-68ca1961275b-request-4/0"
  },
  "timestamp": "2022-10-31T13:06:38.195401296Z",
  "type": "application/json",
  "source": "am-authentication"
}
```
