how tos

Setting Up Progressive Profiles in AM

Overview

Although progressive profiles are shipped with ForgeRock Identity Management (IDM), what if users only regularly log in using ForgeRock Access Management (AM)?

Intelligent authentication trees let you create the same type of workflow, and occasionally ask the user for a little more information so that they don’t have to complete their entire profile at once. 

This article shows you how you can quickly set up progressive profiles with AM using intelligent authentication trees. 

For this example, we’re going to set up a script to evaluate the user’s profile at every fifth login. During this evaluation, if the user is missing their email address, telephone number, or mailing address, we will prompt them for one piece of missing information (for example, a mailing address will be treated as one piece). This would then prompt the user for the next missing piece of information 5 logins later (configurable, of course), so that we’re not making the user fill out a bunch of information all at once. 

Prerequisites

For this article, we have already extended the ForgeRock Directory Services (DS) LDAP schema with an additional attribute to capture the login count. We also added three community nodes to the AM instance from the ForgeRock Marketplace. 

Setting Up Progressive Profiles

The high-level steps to setting up progressive profiles are:

  1. Prepare your schema attributes in DS and AM

  2. Add any custom nodes to your AM instance

  3. Create your scripts and add them to AM

  4. Create your authentication tree(s)

Preparing Your Directory

For this use case, we created a custom attribute in the user store logincount, and added this attribute to the AM Identity store user attribute list. To get up and running quickly in your lab, you could also reuse an existing attribute (not recommended for production). You may also need to add any other attributes that you need to update to the LDAP user attribute list in AM (verify that telephoneNumber, postalAddress, l, st, and postalCode are all listed for this example).  

In our example, we added the following schema file to the opendj/db/schema/ directory, and then restarted ForgeRock DS:

dn: cn=schema
objectClass: top
objectClass: ldapSubentry
objectClass: subschema
cn: schema
attributeTypes: ( 1.3.6.1.4.1.36733.2.2.1.240 NAME 'logincount'    DESC 'login counter'  EQUALITY 2.5.13.2 ORDERING 2.5.13.3 SUBSTR 2.5.13.4 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications X-APPROX '1.3.6.1.4.1.26027.1.4.1' )
objectClasses:  ( progprofileobjectclass NAME 'pprofile' SUP top AUXILIARY DESC 'Progressive Profile Object class' MAY ( logincount ) )

Next, we added the object class and attribute to the AM identity store user configuration:

If you don’t want to add a counter to your user profile store, another option is to use a script to randomly decide whether or not to prompt users for additional information. Or, if you have a better idea, it’s easy to modify the scripted decision node script with the logic to meet your use case.

Adding Your Custom Authentication Nodes

Before setting up the Authentication Tree, you’ll need to add a few ForgeRock Marketplace nodes to your AM instance. Start by following the documentation for each of these nodes, and adding them to AM. You’ll need to restart your AM instance after installing the lib files:

Adding Your Scripts to AM

For the demo, we created three decision node scripts for our authentication trees scripts:

  • AT_ProgressiveProfiles: This script requires that you set sharedState variables for the profile attributes you’re evaluating, as well as the login counter. The script is set to evaluate the attributes every 5 logins (modify per your requirements), and it assumes that you also increment the counter at the completion of each login.
     

  • AT_ProgressiveProfiles-Random: Use this script if you don’t want the login counter, and instead want a random determination. Change the math command-line to change the chance of being prompted for progressive profile completion. 
     

  • AT_IncrementLoginCount: This script requires that you set the login count in the sharedState before running, and it increments the counter and returns the new value to the shared state upon completion:

AT_ProgressiveProfiles
 

/*
  - This script will prompt the user for progressive profile completion based on the number of logins
  - Before this script use the GetProfileProperties node to read in the attributes: 
  -    mail, telephoneNumber, postalAddress
  - logincount (added as a custom attribute)
  - Outcome from this script will be 'continue' if no need to prompt for progressive profile, 
  -    or it will be the next
  - attribute in the priority that is currently blank (priority: mail, telephoneNumber, postalAddress)
  - v 1.0 2019-06-25 Stephen.[email protected]
 */
logger.error('AT_ProgressiveProfiles script start');

/*
 -  set the promptcount to the number of logins you want the user
 */

var promptcount = 5;

var telephoneNumber = sharedState.get("telephoneNumber");
var mail = sharedState.get("mail");
var postalAddress = sharedState.get("postalAddress");
var telephoneNumber = sharedState.get("telephoneNumber");
var logincount = sharedState.get("logincount");
var outcome = "continue"

logger.error('login count: ' + logincount );
logger.error('mail: ' + mail );
logger.error('telephoneNumber: ' + telephoneNumber );
logger.error('postalAddress: ' + postalAddress );
logger.error('promptcount: ' + promptcount);

if (logincount % promptcount ==0 ) {
  outcome = "continue";
  logger.error('will process progressive profile ask');
  if (mail == null) {
    outcome="mail";
    logger.error('Ask for email');
  } else if (telephoneNumber == null) {
    outcome="telephoneNumber";
  } else if (postalAddress == null) {
    outcome="postalAddress";
  }

} else {
  outcome = "continue";
  logger.error("no prompting");
}

 

AT_ProgressiveProfiles-Random
 

/*
  - ScriptP AT_ProgressiveProfiles-Random
  - This script will prompt the user for progressive profile completion based on the number of logins
  - Before this script use the GetProfileProperties node to read in the attributes: 
  -    mail, telephoneNumber, postalAddress
  - This script will randomly determine whether or not to prompt the user rather than using a login counter.
  - Outcome from this script will be 'continue' if no need to prompt for progressive profile, 
  -      or it will be the next
  - attribute in the priority that is currently blank (priority: mail, telephoneNumber, postalAddress)
  - v 1.0 2019-06-25 [email protected]
 */
logger.error('AT_ProgressiveProfiles-Random script start');

var telephoneNumber = sharedState.get("telephoneNumber");
var mail = sharedState.get("mail");
var postalAddress = sharedState.get("postalAddress");
var telephoneNumber = sharedState.get("telephoneNumber");
var outcome = "continue"

if (Math.random() > 0.8) {
  outcome = "continue";
  logger.error('will process progressive profile ask');
  if (mail == null) {
    outcome="mail";
    logger.error('Ask for email');
  } else if (telephoneNumber == null) {
    outcome="telephoneNumber";
  } else if (postalAddress == null) {
    outcome="postalAddress";
  }
} else {
  outcome = "continue";
  logger.error("no prompting");
}

 

AT_IncrementLoginCount
 

/*
  - ScriptP AT_ProgressiveProfiles-Random
  - This script will prompt the user for progressive profile completion based on the number of logins
  - Before this script use the GetProfileProperties node to read in the attributes: 
  -    mail, telephoneNumber, postalAddress
  - This script will randomly determine whether or not to prompt the user rather than using a login counter.
  - Outcome from this script will be 'continue' if no need to prompt for progressive profile, 
  -      or it will be the next
  - attribute in the priority that is currently blank (priority: mail, telephoneNumber, postalAddress)
  - v 1.0 2019-06-25 [email protected]
 */
logger.error('AT_ProgressiveProfiles-Random script start');

var telephoneNumber = sharedState.get("telephoneNumber");
var mail = sharedState.get("mail");
var postalAddress = sharedState.get("postalAddress");
var telephoneNumber = sharedState.get("telephoneNumber");
var outcome = "continue"

if (Math.random() > 0.8) {
  outcome = "continue";
  logger.error('will process progressive profile ask');
  if (mail == null) {
    outcome="mail";
    logger.error('Ask for email');
  } else if (telephoneNumber == null) {
    outcome="telephoneNumber";
  } else if (postalAddress == null) {
    outcome="postalAddress";
  }
} else {
  outcome = "continue";
  logger.error("no prompting");
}

 

Creating Your Authentication Trees

For this example, we created two authentication trees. The first “outer” tree is the main authentication tree, where we ask the user for their username and password, and then validate the credentials. Next, we call the “inner” tree to do the progressive profile checking/updating. This is a very simple example; you can start with the simple example to verify your progressive profiling works, and then later add the inner tree to any of your multi-factor or passwordless authentication flows. By separating out the progressive profiling into a separate inner tree, you can call it from any authentication flow you desire. 

Here is our simple outer tree:

The inner tree will be slightly different depending on whether or not you’re using the login increment counter. 

Assuming you’re using the login counter, your inner tree will look like this: 

Node Details:

  • Start

  • Get Profile Properties {Key, Value}:

    • postalAddress, postalAddress

    • telelphoneNumber, telephoneNumber

    • mail, mail

    • postalCode, postalCode

    • logincount, logincount:


       

  • Scripted Decision Node:

    • Script: AT_ProgessiveProfiles

    • Outcomes:

      • telephoneNumber

      • mail

      • postalAddress

      • Continue
         

  • Input Collector Node - Mail

    • Variable Name: mail

    • Prompt: Please provide your eMail Address

    • Password input: Not selected

    • Use Transient State: Not Selected
       

  • Input Collector Node - Telephone

    • Variable Name: telephoneNumber

    • Prompt: Please provide your telephone number

    • Password input: Not selected

    • Use Transient State: Not Selected
       

  • Page Node:

    • Input Collector Node - Postal Address

      • Variable Name: postalAddress

      • Prompt: Please provide your Address

      • Password input: Not selected

      • Use Transient State: Not Selected
         

    • Input Collector Node - City

      • Variable Name: city

      • Prompt: Please provide your City

      • Password input: Not selected

      • Use Transient State: Not Selected
         

    • Input Collector Node - State

      • Variable Name: state

      • Prompt: Please provide your State

      • Password input: Not selected

      • Use Transient State: Not Selected
         

    • Input Collector Node - Zip

      • Variable Name: zip

      • Prompt: Please provide your zip code

      • Password input: Not selected

      • Use Transient State: Not Selected
         

  • Set Profile Property - Mail {Key, Value}

    • mail, mail
       

  • Set Profile Property - Telephone {Key, Value}

    • telephoneNumber, telephoneNumber
       

  • Set Profile Property - postal Address {Key, Value}

    • postalAddress, postalAddress

    • st, state

    • postalCode, zip

    • l, city

  • Scripted Decision: Increment Scripted Decision

    • Script: AT_IncrementLoginCount

    • Outcomes: continue

    • NOTE: Omit this node if using the Random method
       

  • Set Profile Property {Key, Value}

    • logincount, newlogincount

 

Testing Your Configuration

Use your browser to log in to the outer tree, for example: https://testing.frdpcloud.com/openam/XUI/?service=LoginProgressiveProfile:

 

 

Conclusion

Congratulations on successfully configuring progressive profiles using ForgeRock intelligent authentication trees!