Use case: Configure support for early stage accounts (unauthenticated, unregistered users) in ForgeRock Identity Cloud

Use case overview

A typical use case involves a website user browsing anonymously, adding items to their shopping cart, and then deciding to register before finalizing their purchase. ForgeRock Identity Cloud offers a solution that easily addresses this use case through its Journeys capabilities.

In this article, we’ll demonstrate how Identity Cloud can create a device-dependent browsing history for anonymous users that can later be linked to an account after the user registers using the same device.

We will simulate user browsing across different portals, which creates a “hidden” device-specific account. When a user chooses to register they will be given the option to attach their browsing history to their account. This history may include previous purchases and can be used to customize interactions for a more personalized experience.

Steps to achieve this use case

This example use case has five stages:

  1. Create a list of random URLs from your portal that a user might browse

  2. Create a journey for unknown user browsing

  3. Generate a user browsing history for an unknown user based on their device

  4. Create a journey to promote the unknown user to a known user

  5. Register the user and attach their browsing history

Create a list of random URLs from your portal that a user might browse

In order to simulate an arbitrary user history, create a list of random URLs from your portal that a user might browse. This list of random URLs will be used later in the simulation by defining the target URL in the journey used by unknown users.

For our example, we created the following list of URLs for the car manufacturers Toyota and Lexus:

https://www.toyota.com/upcoming-vehicles/gr86
https://www.toyota.com/prius
https://www.toyota.com/upcoming-vehicles/rav4-hybrid-woodland-edition/ 
https://www.toyota.com/mirai
https://www.lexus.com/models/LC-hybrid
https://www.lexus.com/models/UX-hybrid/offers
https://www.lexus.com/models/RX-hybrid
https://www.lexus.com/models/ES-hybrid/offers
https://www.lexus.com/models/LS-hybrid

Create a journey for unknown user browsing (HiddenRegistrationAndLogins)

This journey is invoked behind the scenes when a user browses a website. In our example, the journey is called HiddenRegistrationAndLogins.

The journey begins by collecting a unique identifier for the user’s device. If an account doesn’t already exist it creates a pseudonymous account. The user’s browsing history and shopping cart are stored with this account. If an account already exists, the browser history is updated by adding additional attribute information to the user’s profile.

Create the journey scripts

First, you will need to create the following three scripts:

  • Pre-fill Required Attributes
  • SetDestinationUrl
  • SaveHistory

Pre-fill Required Attributes

Create the following Pre-fill Required Attributes script. This script generates the userName from the device ID generated from the Device Profile Collector node and sets some values for the required attributes (defined in your data model):

var objectAttributes = sharedState.get("objectAttributes");
if (objectAttributes == null) objectAttributes = new java.util.LinkedHashMap();

var thisDevice = sharedState.get("forgeRock.device.profile");
var userName = thisDevice.get("identifier");

sharedState.put("userName", userName);
sharedState.put("username", userName);

objectAttributes.put('userName', userName);
objectAttributes.put("mail", "user@example.com");
objectAttributes.put("givenName", "Please provide a value");
objectAttributes.put("sn", "Please provide a value");

sharedState.put("objectAttributes", objectAttributes);

outcome = "true";

SetDestinationURL

Create a SetDestinationURL script, similar to this example:

var turls = [
  'https://www.example.com/example',
  'https://www.toyota.com/upcoming-vehicles/gr86',
  'https://www.toyota.com/prius',
  'https://www.toyota.com/upcoming-vehicles/rav4-hybrid-woodland-edition/',
  'https://www.toyota.com/mirai'
];

var lurls = [
  'https://www.lexus.com/models/LC-hybrid',
  'https://www.lexus.com/models/UX-hybrid/offers',
  'https://www.lexus.com/models/RX-hybrid',
  'https://www.lexus.com/models/ES-hybrid/offers',
  'https://www.lexus.com/models/LS-hybrid'
];

if (requestParameters.get("brand")!==null && requestParameters.get("brand").get(0)!==null){
  var brand = decodeURIComponent(requestParameters.get("brand").get(0));
} else {
  var brand = "toyota";
}

if (brand=="toyota"){
  var dest = turls[Math.floor(Math.random() * turls.length)];
} else {
  var dest = lurls[Math.floor(Math.random() * lurls.length)];
};

sharedState.put("successUrl", dest);
sharedState.put("failureUrl", dest);

outcome = "true";

This script randomly chooses the destination URL to simulate an arbitrary users’ behavior after the hidden registration (the URL is also used if registration fails). In our example, the script contains portal URLs for the car manufacturers, Toyota and Lexus.

This script is also aware of a request parameter called brand. If this request parameter is provided, the script would choose only URLs for the provided brand (either Toyota or Lexus). This demonstrates how the generation of the browsing history can be fine-tuned. In this case it’s brand; in other cases it might be by a certain marketing campaign or region/location, for example.

You can replace these URLs with your own examples.

By calling the journey several times, the browser will be redirected to randomly picked URLs, which simulate the browsing behavior of an unknown user. These URLs are stored in the history of the user and are device-dependent.

The destination is stored in sharedState variables successUrl and failureUrl to redirect the user to the destination.

NOTE: In a real-world scenario, the URLs for the history would be generated, for example by an Identity Gateway that sits in front of the destination portal.

SaveHistory

Create the following SaveHistory script. This script saves the browsing history in the general purpose attribute fr-attr-imulti1:

var fr = JavaImporter(
  org.forgerock.openam.auth.node.api,
  com.sun.identity.authentication.callbacks.ScriptTextOutputCallback
);
with (fr) {
  var brand = requestParameters.get("brand")
  if (brand) {
    brand = requestParameters.get("brand").get(0)
  } else {
    brand = "toyota";
  };
  // Create new entry with device profile, url and date
  var profile = sharedState.get("forgeRock.device.profile");
  var date = Date.now();
  var brand = sharedState.get("brand");
  var url = sharedState.get("successUrl");
  var identifier = profile.get("identifier");
  var platform = profile.get("metadata").get("platform").get("platform");
  var deviceName = profile.get("metadata").get("platform").get("deviceName");
  var latitude = profile.get("location").get("latitude");
  var longitude = profile.get("location").get("longitude");
  var userAgent = profile.get("metadata").get("browser").get("userAgent");
  var browser = "";
  
  // Work out which browser this is
  if (userAgent.toUpperCase().indexOf("FIREFOX") > -1) {
    browser = "Firefox";
  } else if (userAgent.toUpperCase().indexOf("CHROME") > -1 && userAgent.toUpperCase().indexOf("SAFARI") > -1) {
    // Chrome can sometimes list Chrome followed by Safari in userAgent
    if (userAgent.toUpperCase().indexOf("CHROME") < userAgent.toUpperCase().indexOf("SAFARI")) {
      browser = "Chrome";
    }
  } else if (userAgent.toUpperCase().indexOf("CHROME") > -1) {
    browser = "Chrome";
  } else if (userAgent.toUpperCase().indexOf("SAFARI") > -1) {
    browser = "Safari";
  }
  
  var entry = {
    "date": Date().toString(),
    "brand": brand,
    "url": url,
    "lastSelectedDate": date,
    "device": {
      "identifier": String(identifier),
      "metadata": {
        "platform": {
          "platform": String(platform),
          "deviceName": String(deviceName)
        },
        "browser": browser
      }
    },
    "location": {
      "latitude": String(latitude),
      "longitude": String(longitude)
    }
  }
  
  // Add entry to multi value
  var username = sharedState.get("_id");
  // var username = sharedState.get("userName");
  var attribute = "fr-attr-imulti1"
  idRepository.addAttribute(username, attribute, JSON.stringify(entry));
  
  // add device name to shared state 
  sharedState.put("deviceName", "Default");
  
  outcome = "true"
}

The contents of the browsing history is customizable. In this example, we are storing the brand and the URL that was visited. Additionally, the sharedState variable deviceName is set, which is used in the Device Profile Save node

Create the journey

  1. Sign in to the Identity Cloud admin UI using your admin tenant URL, in the format https://<tenant-name>/am/XUI/?realm=/#/.

  2. Go to Journeys > New Journey.

  3. Enter a unique name for the journey (for example, HiddenRegistrationAndLogins), select which identities will authenticate using this journey, (optionally) enter a journey description, and click Save.

  4. Create a journey similar to this:

    Node descriptions:

    • Device Profile Collector - Gathers metadata about the device used to authenticate. The device location may also be collected. See Device Profile Collector node for further details.

    • Pre-fill required attributes - This Scripted Decision node generates the userName from the device ID generated from the Device Profile Collector node and sets some values for the required attributes. The same userName is generated each time the user logs in from the same device, which creates the user’s browsing history. See Step 5 for information on configuring this node.

    • Set Destination - This Scripted Decision node simulates the browsing behavior of the unknown user. It randomly chooses the destination URL after the hidden registration. See Step 6 for information on configuring this node.

    • Known device? - This Identify Existing User node attempts to identify the user’s device. See Step 7 for information on configuring this node.

    • Save Browsing History - This Scripted Decision node saves the user’s browsing history. See Step 8 for information on configuring this node.

    • Device Profile Save - Persists collected device data to a user’s profile in the identity store. See Device Profile Save node for further information. See Step 9 for information on configuring this node.

    • Create Object - Creates a new user object in Identity Cloud if the device cannot be recognized. The Identity Resource matches must match the user object, for example, managed/alpha_user (the default is managed/user). See Create Object node for further information.

    • Get ID - This Identify Existing User node ensures that the sharedState variable _id is present. This variable is used in the SaveHistory script to identify the user to whom the “browsing” entry is written. See Step 10 for information on configuring this node.

  5. Click the Pre-fill required attributes node (Scripted Decision node) and configure the node as follows:

    • Script: Select the Pre-fill Required Attributes script you created previously.
    • Outcomes: Enter true.

  6. Click the initial Set Destination node (Scripted Decision node) and configure the node as follows:

    • Script: Select the SetDestinationUrl script you created previously.
    • Outcomes: Enter true.

  7. Click the Known Device? node (Identity Existing User node) and set the Identity Attribute to userName.

    uc_known_device_node

  8. Click the Save Browsing History node (Scripted Decision node) and configure the node as follows:

    • Script: Select the SaveHistory script you created previously.
    • Outcomes: Enter true.

  9. Click the Device Save Profile node and set deviceName as the Device Name Variable. This ensures that the device is visible (named Default) to the user after registration.

    uc_device_save_profile_node

  10. Click the Get ID node (Identify Existing User node) and set the Identity Attribute to userName.

    uc_get_id_node

  11. Click Save to save the journey.

Generate a user browsing history for an unknown user based on their device

In this step we’ll run through the HiddenRegistrationAndLogins journey several times using the same device. This will create a new unknown user and the browsing history for that user.

  1. In the Identity Cloud admin UI, go to Journeys.

  2. Click the HiddenRegistrationAndLogins journey (you created previously) and copy the Preview URL.

    uc_preview_url

  3. Paste the preview URL into a browser using Incognito or Browsing mode.

    Without any system interaction, one of the randomly chosen portal URLs should open.

  4. Repeat Step 3 several times from the same device to generate a browsing history.

An unknown (hidden) user is created in Identity Cloud. To view this user:

  1. In the Identity Cloud admin UI, go to Identities > Manage.

    The new unknown user should appear in the list and will look similar to this:

  2. Scroll down to view the user’s browsing history, for example:

    uc_new_unknown_user_browse_history

Create a journey to promote the unknown user to a known user (PromoteUnknowUserToKnown)

At some stage, the user may choose to register on the website. This is achieved using another journey, which we’ve named PromoteUnknowUserToKnown.

This journey starts by identifying the user’s device. It then creates or derives a device ID and checks if the device has been seen before (by checking the existence of an anonymous account).

If the device is known, the existing identity object for the unknown user is patched with data the user provides during a registration step. In this case, the username and other generated information are removed from the user (using the Clean User Data scripted node). The user is prompted to choose between creating a new clean profile or retaining their browsing and shopping cart history. Note that a real-world scenario would not typically include this choice step.

If the device is unknown, a new user is created with the data the user provides in a registration step.

Then the login counter is incremented and the Inner Tree for progressive profiling is evaluated. The user is logged in and forwarded to the end user dashboard.

Create the journey scripts

First, you will need to create the following three scripts:

  • IdentifyDeviceAsUsername
  • RemovePreviouslyGeneratedData
  • CleanHistory

IdentifyDeviceAsUsername

Create the following IdentifyDeviceAsUsername script. This script generates the userName from the device ID generated from the Device Profile Collector node:

var objectAttributes = sharedState.get("objectAttributes");
if (objectAttributes == null) objectAttributes = new java.util.LinkedHashMap();

var thisDevice = sharedState.get("forgeRock.device.profile");
var userName = thisDevice.get("identifier");

objectAttributes.put('userName', userName);
sharedState.put("objectAttributes", objectAttributes);
sharedState.put("userName", userName);

// add device name to shared state
sharedState.put("deviceName", "Default");

outcome = "true";

RemovePreviouslyGeneratedData

Create the following RemovePreviouslyGeneratedData script. This script removes the username and other generated data from the user’s profile if the device is already known.

objectAttributes = sharedState.get("objectAttributes")
userId = sharedState.get("_id")

if(userId){
  objectAttributes.put("_id", userId)
  objectAttributes.remove("mail")
  objectAttributes.remove("userName")
  objectAttributes.remove("givenName")
  objectAttributes.remove("sn")
  sharedState.put("objectAttributes", objectAttributes);
}

outcome = "true";

Note that the userID is preserved.

CleanHistory

Create the following CleanHistory script. This script removes the user’s previously generated history. The multi-valued attribute fr-attr-imulti1 will be overwritten with the new entry for the landing page.

var fr = JavaImporter(
  org.forgerock.openam.auth.node.api,
  com.sun.identity.authentication.callbacks.ScriptTextOutputCallback
);
with (fr) {
  var brand = requestParameters.get("brand")
  if (brand) {
    brand = requestParameters.get("brand").get(0)
  } else {
    brand = "toyota";
  };
  // Create new entry with device profile, url and date
  var profile = sharedState.get("forgeRock.device.profile");
  var date = Date.now();
  var brand = sharedState.get("brand");
  var url = "https://openam-forr-dev.forgeblocks.com/enduser/?realm=alpha#/dashboard";
  //var url = sharedState.get("successUrl");
  var identifier = profile.get("identifier");
  var platform = profile.get("metadata").get("platform").get("platform");
  var deviceName = profile.get("metadata").get("platform").get("deviceName");
  var latitude = profile.get("location").get("latitude");
  var longitude = profile.get("location").get("longitude");
  var userAgent = profile.get("metadata").get("browser").get("userAgent");
  var browser = "";
  
  // Work out which browser this is
  if (userAgent.toUpperCase().indexOf("FIREFOX") > -1) {
    browser = "Firefox";
  } else if (userAgent.toUpperCase().indexOf("CHROME") > -1 && userAgent.toUpperCase().indexOf("SAFARI") > -1) {
    // Chrome can sometimes list Chrome followed by Safari in userAgent
    if (userAgent.toUpperCase().indexOf("CHROME") < userAgent.toUpperCase().indexOf("SAFARI")) {
      browser = "Chrome";
    }
  } else if (userAgent.toUpperCase().indexOf("CHROME") > -1) {
    browser = "Chrome";
  } else if (userAgent.toUpperCase().indexOf("SAFARI") > -1) {
    browser = "Safari";
  }
  
  var entry = {
    "date": Date().toString(),
    "brand": brand,
    "url": url,
    "lastSelectedDate": date,
    "device": {
      "identifier": String(identifier),
      "metadata": {
        "platform": {
          "platform": String(platform),
          "deviceName": String(deviceName)
        },
        "browser": browser
      }
    },
    "location": {
      "latitude": String(latitude),
      "longitude": String(longitude)
    }
  }
  
  // Add entry to multi value
  var username = sharedState.get("_id");
  // var username = sharedState.get("userName");
  var attribute = "fr-attr-imulti1"
  idRepository.setAttribute(username, attribute, [JSON.stringify(entry)]);
  
  // add device name to shared state 
  sharedState.put("deviceName", "Default");
  
  outcome = "true"
}

Create the journey

  1. Sign in to the Identity Cloud admin UI using your admin tenant URL, in the format https://<tenant-name>/am/XUI/?realm=/#/.

  2. Go to Journeys > New Journey.

  3. Enter a unique name for the journey (for example, PromoteUnknowUserToKnown), select which identities will authenticate using this journey, (optionally) enter a journey description, and click Save.

  4. Create a journey similar to this:

    Node descriptions:

    • Device Profile Collector - Collects the device profile for the user. See Device Profile Collector node for further details.

    • Lookup Device - This Scripted Decision node checks the username associated with the device. See Step 5 for information on configuring this node.

    • Seen Before? - This Identify Existing User node identifies if the user is known or unknown. See Step 6 for information on configuring this node.

    • Clean User Data - This Scripted Decision node removes the username and other generated data from the user’s profile if the user is already known. See Step 7 for information on configuring this node.

    • Page Node node descriptions:

      • Platform Username - Prompts the user to enter their username, and stores it in a configurable state attribute. See Platform Username node for more information.
      • Platform Password - Collects the user’s password. See Platform Password node for further information.
      • Attribute Collector - Collects the values of attributes to populate the new account. See the Attribute Collector node for further information.
      • Accept Terms and Conditions - Verifies the user has accepted the active set of terms and conditions. See Terms and Conditions Decision node for more information.
    • Patch Object - Patches the existing user with the data the user provided. See Step 8 for information on configuring this node.

    • Create Object - Creates a new user object in Identity Cloud based on information collected during registration. See Create Object node for further information.

    • Clear History? - This Message node asks the user if they want to clear their history. See Step 9 for information on configuring this node.

    • Clear History - This Scripted Decision node removes the user’s previously generated browsing history. See Step 10 for information on configuring this node.

    • Save History - This Scripted Decision node saves the user’s browsing history. See Step 11 for information on configuring this node.

    • Create Object - Creates a new user object in Identity Cloud if the device cannot be recognized. See Create Object node for further information.

    • Increment Login Count - Increments the successful login count property of a managed object. See Increment Login Count node for further information.

    • Inner Tree Evaluator - This Inner Tree Evaluator node nests an authentication flow as a child within the journey. See Step 12 for information on configuring this node.

  5. Click the Lookup Device node (Scripted Decision node) and configure the node as follows:

    • Script: Select the IdentityDeviceAsUsername script you created previously.
    • Outcomes: Enter true.

  6. Click the Seen Before? node (Identify Existing User node) and set the Identity Attribute to userName.

    uc_seen_before_node

  7. Click the Clean User Data node (Scripted Decision node) and configure the node as follows:

    • Script: Select the IdentityDeviceAsUsername script you created previously.
    • Outcomes: Enter true.

  8. Click the Patch Object node and set the Identity Attribute to _id.

  9. Click the Clear History? node (Message node) and add a message asking the user if they want to clear their browsing history, for example, Do you want to clear your browsing history?

  10. Click the Clean History node (Scripted Decision node) and configure the node as follows:

    • Script: Select the CleanHistory script you created previously.
    • Outcomes: Enter true.

  11. Click the Save History node (Scripted Decision node) and configure the node as follows:

    • Script: Select the SaveHistory script you created for the other journey.
    • Outcomes: Enter true.

  12. Click the Progressive Profile node (Inner Tree node) and select the ProgressiveProfile journey.

    uc_prog_profile_node

  13. Click Save to save the journey.

Register the user and attach their browsing history

In this step, we’ll run through the PromoteUnknowUserToKnown journey to register the unknown user and attach their browsing history to their account.

Before performing this step, clear the cookies and browsing history from the device.

  1. In the Identity Cloud admin UI, go to Journeys.

  2. Click the PromoteUnknowUserToKnown journey (you configured previously) and copy the Preview URL.

  3. Paste the preview URL into a browser using Incognito or Browsing mode.

    You are presented with a registration page, similar to this:

  4. Complete the fields and click Next.

  5. Click No when prompted to clear the history (this step is just for demonstration purposes and is not likely to be implemented in a real-world scenario).

    You are redirected to the user’s dashboard.

  6. Click Edit Your Profile

    Note the Trusted Device named Default, which was automatically generated.

  7. Click Edit Personal Info to view the user’s browsing history.

Conclusion

This example use case has demonstrated how to create a device-dependent browsing history for an anonymous user, which can later be linked to an account when the user registers using the same device. This use case can be achieved with the Journeys capabilities of Identity Cloud.

For further information on ForgeRock’s intelligent access journeys, go to https://www.forgerock.com/platform/access-management/intelligent-access.

Additional resources

Documentation:

Acknowledgements: Andre Posner