Using an Authentication Tree Stage to Build a Custom UI with the ForgeRock JavaScript SDK

The ForgeRock JavaScript SDK greatly simplifies the process of adding intelligent authentication to your applications. It offers a ready-to-use UI that completely handles rendering of authentication steps. You can also take full control and create a custom UI; in which case, it’s helpful to know the current stage of the authentication tree to determine which UI you should render.

OpenAM 7.0 adds a stage property to the page node that can be used for this purpose, and alternate approaches are available for prior versions. This post will show you two approaches for OpenAM 6.5, and one for OpenAM 7.

OpenAM 6.5

While the stage property doesn’t exist in authentication trees prior to OpenAM 7.0, there are two alternate approaches to achieve the same result.

Approach #1: Metadata Callbacks

Using metadata callbacks, you can inject the stage value into the tree’s response payload. The only difference is that the value will appear in a callback, instead of directly associated with the step itself. This approach involves three steps:

  1. Create a script to add the metadata callback.
  2. Update your tree to execute that script.
  3. Read the metadata callback in your application.

Step 1: Add a Metadata Callback Using a Script

  • Create a script of type Decision node script for authentication trees.
  • Give it an appropriate name, such as “MetadataCallback: UsernamePassword”.
  • In the script, add a metadata callback that creates an object with a stageproperty. Be sure to also set the outcome value:
var fr = JavaImporter(

with (fr) {
  var json = JsonValue.json({ stage: "UsernamePassword" });
  action = Action.send(new MetadataCallback(json)).build();

outcome = "true";

As with all scripts, ensure you have whitelisted any imported classes.

Step 2: Update Your Tree to Execute the Script

Add a scripted decision node to your page node and configure it to reference the script created in the previous step. In this example, the step payload will contain three callbacks:

  • MetadataCallback
  • NameCallback
  • PasswordCallback

Step 3: Read the Metadata Callback

Use the SDK to find the metadata callback and read its stage property:

function getStage(step) {
  // Get all metadata callbacks in the step
  const metadataCallbacks = step.getCallbacksOfType(CallbackType.MetadataCallback);

  // Find the first callback that contains a "stage" value in its data
  const stage = metadataCallbacks
    .map(x => {
      const data = x.getData();
      const dataIsObject = typeof data === "object" && data !== null;
      return dataIsObject && data.stage ? data.stage : undefined;
    .find(x => x !== undefined);

  // Return the stage value, which will be undefined if none exists
  return stage;

Approach #2: Inspecting Callbacks

If you have relatively few and/or well-known authentication trees, it’s likely you can determine the stage by simply looking at the types of callbacks in the step.

For example, it’s common for a tree to start by capturing the username and password. In this case, you can inspect the callbacks to see if they consist of a NameCallback and PasswordCallback. If your tree uses WebAuthn for passwordless authentication, the SDK can help with this inspection:

function getStage(step) {
  // Check if the step contains callbacks for capturing username and password
  const usernameCallbacks = step.getCallbacksOfType(CallbackType.NameCallback);
  const passwordCallbacks = step.getCallbacksOfType(CallbackType.PasswordCallback);
  if (usernameCallbacks.length > 0 && passwordCallbacks.length > 0) {
    return "UsernamePassword";

  // Use the SDK to determine if this is a WebAuthn step
  const webAuthnStepType = FRWebAuthn.getWebAuthnStepType(step);
  if (webAuthnStepType === WebAuthnStepType.Authentication) {
    return "DeviceAuthentication";
  } else if (webAuthnStepType === WebAuthnStepType.Registration) {
    return "DeviceRegistration";

  // ... Add checks to determine other stages in your trees ...

  return undefined;

OpenAM 7.0 Approach

Using OpenAM 7.0 to specify a stage is straightforward. When constructing a tree, place nodes inside a page node and then specify its stage, which is a free-form text field:

When you use the SDK’s FRAuth module to iterate through a tree, you can now call the getStage() method on the returned FRStep, and decide which custom UI needs to be rendered:

// Get the current step in the tree
const currentStep = await;

// Use the stage value configured in the tree
switch (currentStep.getStage()) {
  case "UsernamePassword":
    // Render your custom username/password UI
  case "SomeOtherStage":
    // etc