Authentication nodes

Backchannel Notification node

RAPID only

The Backchannel Notification node adds data to the backchannel transaction from the backchannel journey, which the Backchannel Status node can read. This lets you send real-time status updates to the main journey from the backchannel journey.

Example

The following example shows how the Backchannel Notification node can be used in an out-of-band authentication journey to keep a help desk agent informed of progress while they’re verifying a user’s identity. The help desk agent can see real-time status updates when the user enters their password and when their credentials have been confirmed.

The example shows two journeys:

  • The main journey initializes a backchannel authentication journey and displays status updates to the help desk agent.

  • The backchannel journey is a simple authentication journey that adds status updates to the shared state and uses the Backchannel Notification node to add this data to the backchannel transaction.

Main journey

Example help desk agent journey

a The Page node with an Attribute Collector node prompts the help desk agent for the end user’s email address.

b The Identify Existing User node attempts to look up the username by matching the email address to the email address in an identity profile.

The lookup fails if no matching email address is found, or if more than one user profile uses the same email address.

c The Set Backchannel State Properties custom node writes the _id, objectAttributes, and username to the shared state.

This example uses the following configuration:

Attribute list to set

_id
objectAttributes
username

Backchannel nodeState property

backchannel:data

How do I create this custom node?

The following steps provide the minimum configuration required to create this custom node. Learn more in custom nodes.

  1. In the Advanced Identity Cloud admin UI, go to Journeys > Custom Nodes and click New Custom Node.

  2. On the New Custom Node page, enter Set Backchannel State Properties as the name and click Next.

  3. Add the following properties to the node and click Next:

    Attribute list to set
    • Name: attributeListProperty

    • Label: Attribute list to set

    • Type: String

    • Multi-Valued: Select this option.

    • Required: Select this option.

    Backchannel nodeState property
    • Name: backchannelStateProperty

    • Label: Backchannel nodeState property

    • Type: String

    • Required: Select this option.

    • Provide Default Value: Select this option.

    • Default Value: backchannel:data

  4. Set the following settings and click Next:

    Outcomes

    Next

    Error Outcome

    Enabled

  5. In the script editor, add the following script to define the behavior of the node:

    (function () {
      logger.error(scriptName + "Node execution started");
    
      //Get node properties
      var nodeStateProperty = properties.backchannelStateProperty;
      var attributeList = properties.attributeListProperty;
    
      //Loop to get and set attributes into the nodeStateProperty nodeState variable
      var out = {};
      for (var i = 0; i < attributeList.length; i++) {
        var k = attributeList[i];
        var v = nodeState.get(k);
        if (v !== null && v !== undefined) out[k] = v;
      }
      nodeState.putShared(nodeStateProperty, out);
        logger.error(scriptName + "Node execution completed");
      action.goTo("Next");
    })();
  6. Click Save to create the custom node.

d The Backchannel Initialize node reads the username from the shared state.

  • If the username is valid, the node generates a redirect URI to start the backchannel authentication journey. The node writes the redirect URI and the transaction ID of the backchannel transaction to the shared state, and the journey proceeds to the Backchannel Status node.

  • If the username can’t be read, the journey follows the Error outcome and fails.

e The Backchannel Status node reads the transaction ID and provides status on the authentication request.

This example requires the following configuration:

Record Transaction Data

Enabled

Transaction Data Key

transaction:data

f The Pending Wait node is a Polling Wait node that pauses this journey when the backchannel authentication request has a status of Pending. After 3 seconds, the journey returns to the Backchannel Status node.

g The In Progress Wait node is a Configuration Provider node. It imitates a Polling Wait node and uses a script to display messages from the backchannel journey to the help desk agent when the backchannel authentication has a status of In progress.

Sample Config Provider node script
var message;
var data = nodeState.get("transaction:data");
//Make sure this property name matches the Transaction Data Key configured in the Backchannel Status node
if (data) {
    message = data["description"];
} else {
    message = "Authentication in progress...";
}
config = {
    "secondsToWait": 3,
    "spamDetectionEnabled": false,
    "spamDetectionTolerance": 3,
    "waitingMessage": { "en": message },
    "exitable": false,
    "exitMessage": {}
};

h The User Message To Display custom node displays a message to the help desk agent when the backchannel authentication completes successfully.

This example uses the following configuration:

Message to display

User successfully authenticated

How do I create this custom node?

The following steps provide the minimum configuration required to create this custom node. Learn more in custom nodes.

  1. In the Advanced Identity Cloud admin UI, go to Journeys > Custom Nodes and click New Custom Node.

  2. On the New Custom Node page, enter User Message to Display as the name and click Next.

  3. Add the following property to the node and click Next:

    Message to display
    • Name: message

    • Label: Message to display

    • Type: String

    • Required: Select this option.

  4. Set the following settings and click Next:

    Outcomes

    Success

  5. In the script editor, add the following script to define the behavior of the node:

    (function () {
      logger.error(scriptName + "Node execution started");
    
      if (callbacks.isEmpty()) {
        var userMessage = properties.message;
        callbacksBuilder.textOutputCallback(0, userMessage);
      } else {
        logger.error(scriptName + "Node execution completed");
        action.goTo("Success");
      }
    })();
  6. Click Save to create the custom node.

Backchannel journey

This is a basic authentication journey that takes credentials and authenticates the user based on their existence in the backend identity store.

Backchannel authentication journey with notifications

a The Page node includes a Display Username node and a Platform Password node. The username has been supplied in the shared state from the main journey. The user needs to enter their password.

b The Set Shared Or Transient State custom node adds data to the shared state that the Backchannel Notification node can send to the main journey.

This example uses the following configuration:

State to update

SHARED

List of values to add to state

transaction:notify.description=User has entered their credentials

How do I create this custom node?

The following steps provide the minimum configuration required to create this custom node. Learn more in custom nodes.

  1. In the Advanced Identity Cloud admin UI, go to Journeys > Custom Nodes and click New Custom Node.

  2. On the New Custom Node page, enter Set Shared Or Transient State as the name and click Next.

  3. Add the following properties to the node and click Next:

    State to update
    • Name: state

    • Label: State to update

    • Description: nodeState.putShared or nodeState.putTransient

    • Type: String

    • Required: Select this option.

    • Enumerated Values: Select this option and add the following key:value pairs:

      • SHARED:SHARED

      • TRANSIENT:TRANSIENT

    • Provide Default Value: Select this option.

    • Default Value: SHARED

    List of values to add to state
    • Name: values

    • Label: List of values to add to state

    • Description: In the form \"key=value\", \"key.subkey=value\", \"key=$key.subkey.subsubkey\", etc

    • Type: String

    • Multi-Valued: Select this option.

    • Required: Select this option.

  4. Set the following settings and click Next:

    Outcomes

    Success
    Error

  5. In the script editor, add the following script to define the behavior of the node:

    var outcome = "Success";
    
    // ---------- helpers ----------
    function isObject(x){ return x !== null && typeof x === "object"; }
    function toStr(x){ return (x === null || x === undefined) ? "" : String(x); }
    
    function resolveFromNodeState(path) {
        var parts = String(path).trim().split(".");
        if (parts.length === 0) return undefined;
        var base = nodeState.get(parts[0]);
        if (parts.length === 1) return base;
    
        var cur = base;
        for (var i = 1; i < parts.length; i++) {
            if (!isObject(cur)) return undefined;
            cur = cur[parts[i]];
            if (cur === undefined || cur === null) return cur;
        }
        return cur;
    }
    
    function substitute(template) {
        try {
            return String(template).replace(/\$\{([^}]+)}/g, function(match, key){
                var val = resolveFromNodeState(key);
                return (val !== undefined && val !== null) ? String(val) : match;
            });
        } catch (e) {
            logger.error("Substitution error: " + e);
            outcome = "Error";
            return template;
        }
    }
    
    function putState(stateName, key, value) {
        if (stateName === "SHARED") {
            nodeState.putShared(key, value);
        } else if (stateName === "TRANSIENT") {
            nodeState.putTransient(key, value);
        } else {
            logger.error("Invalid properties.state: " + stateName);
            outcome = "Error";
        }
    }
    
    function maybeJson(value) {
        var s = String(value).trim();
        if ((s.charAt(0) === "{" && s.charAt(s.length - 1) === "}") ||
            (s.charAt(0) === "[" && s.charAt(s.length - 1) === "]")) {
            try { return JSON.parse(s); } catch (e) {}
        }
        return value;
    }
    
    // Define BEFORE use to avoid issues
    var processPair = function(stateName, pair) {
        try {
            var str = String(pair);
            var eqIdx = str.indexOf("=");
            if (eqIdx < 0) {
                logger.error("Invalid key=value pair (no '='): " + str);
                outcome = "Error";
                return;
            }
            var lh = str.substring(0, eqIdx).trim();
            var rh = str.substring(eqIdx + 1).trim();
            var result = maybeJson(substitute(rh));
    
            var dotIdx = lh.indexOf(".");
            if (dotIdx >= 0) {
                var baseKey = lh.substring(0, dotIdx);
                var subKey  = lh.substring(dotIdx + 1);
                var current = nodeState.get(baseKey);
                if (!isObject(current)) current = {};
                current[subKey] = result;
                putState(stateName, baseKey, current);
            } else {
                putState(stateName, lh, result);
            }
        } catch (e) {
            logger.error("Error processing pair '" + pair + "': " + e);
            outcome = "Error";
        }
    };
    
    // ---------- main ----------
    try {
        var stateName = properties.state;
        var values = properties.values;
    
        if (!values) {
            logger.error("properties.values is empty");
            outcome = "Error";
        } else if (values.iterator && typeof values.length !== "number") {
            var it = values.iterator();
            while (it.hasNext()) {
                processPair(stateName, String(it.next()));
            }
        } else {
            for (var i = 0; i < values.length; i++) {
                processPair(stateName, String(values[i]));
            }
        }
    } catch (outerError) {
        logger.error("Unexpected error: " + outerError);
        outcome = "Error";
    }
  6. Click Save to create the custom node.

c The Backchannel Notification node reads the shared state and sends a status update to the main journey to inform the help desk agent that the user has entered their credentials.

d The Data Store Decision node validates the username-password credentials.

e The Set Shared Or Transient State custom node adds data to the shared state that the Backchannel Notification node can send to the main journey. This is the custom node you created earlier in this example.

This instance of the node uses the following configuration:

State to update

SHARED

List of values to add to state

transaction:notify.description=user’s credentials are valid

f The Backchannel Notification node reads the shared state and sends a status update to the main journey to inform the help desk agent that the user’s credentials match those stored in the data store.

Availability

Product Available?

PingOne Advanced Identity Cloud

Yes

PingAM (self-managed)

Yes

Ping Identity Platform (self-managed)

Yes

Inputs

This node reads data from the incoming node state using the key specified in the Data Key. The key value must be a JSON object.

Implement a node earlier in the journey to add data to the incoming node state using this key. For example, you could use custom nodes or a Scripted Decision node.

Dependencies

This node has no dependencies.

Configuration

Property Usage

Data Key

The shared state key that contains the data you want to add to the backchannel transaction. For example transaction:notify.

This key is used to read data from the incoming node state.

Outputs

This node doesn’t change the shared state.

Callbacks

This node doesn’t send any callbacks.

Outcomes

Success

The backchannel notification was successfully sent.

Error

An error occurred when sending the backchannel notification.

Errors

The node can log the following errors:

  • Failed to update backchannel transaction

    An error occurred when the node attempted to update the backchannel transaction with the data from the incoming node state.

The node can log the following warnings:

  • Transaction ID not found in context

    The transaction ID is missing from the incoming request context.

  • Data in state at dataKey data key was not found or incorrect type, not updating transaction.

    The Data Key configured for the node doesn’t exist in the incoming node state, or it exists but contains invalid data.