ForgeRock Developer Experience

Step 3. Develop the client app

Integrating your application with PingOne Protect enables you to perform risk evaluations during your customer’s journey.

Add code for the following tasks to fully integrate with PingOne Protect:

Initialize data collection

You must initialize the PingOne Signals SDK so that it collects the data needed to evaluate risk.

The earlier you can initialize the PingOne Signals SDK, the more data it can collect to make a risk evaluation.

You can initialize the PingOne Signals SDK by using the start() method, which supports the following parameters:

Parameter

Description

Android

iOS

JavaScript

envID

Required. Your PingOne environment identifier.

deviceAttributesToIgnore

Optional. A list of device attributes to ignore when collecting device signals.

For example, AUDIO_OUTPUT_DEVICES or IS_ACCEPT_COOKIES.

isBehavioralDataCollection

behavioralDataCollection

When true, collect behavioral data.

Default is true.

isConsoleLogEnabled

consoleLogEnabled

When true, output SDK log messages in the developer console.

Default is false.

isLazyMetadata

lazyMetadata

When true, calculate metadata on demand rather than automatically after calling start.

Default is false.

N/A

deviceKeyRsyncIntervals

Number of days that device attestation can rely upon the device fallback key.

Default: 14

N/A

disableHub

When true, the client stores device data in the browser’s localStorage only.

When false the client uses an iframe.

Default is false.

N/A

disableTags

When true, the client does not collect tag data.

Tags are used to record the pages the user visited, forming a browsing history.

Default is false.

N/A

enableTrust

When true, tie the device payload to a non-extractable crypto key stored in the browser for content authenticity verification.

Default is false.

N/A

externalIdentifiers

Optional. A list of custom identifiers that are associated with the device entity in PingOne Protect.

N/A

hubUrl

Optional. The iframe URL to use for cross-storage device IDs.

N/A

waitForWindowLoad

When true, initialize the SDK on the load event, instead of the DOMContentLoaded event.

Default is true.

There are two options for initializing the PingOne Signals SDK:

Initialize manually

Call the start() method before users start interacting with your application to gather the most data and make the most informed risk evaluations.

Pass in the configuration parameters as required.

  • Android

  • iOS

  • JavaScript

try {
  val params =
    PIInitParams(
      envId = "3072206d-c6ce-4c19-a366-f87e972c7cc3",
    )
  PIProtect.start(context, params)
  Logger.info("Settings Protect", "Initialize succeeded")
} catch (e: Exception) {
  Logger.error("Initialize Error", e.message)
  throw e
}
let initParams = PIInitParams(envId: "3072206d-c6ce-4c19-a366-f87e972c7cc3")
  PIProtect.start(initParams: initParams) { error in
    if let error = error as? NSError {
        FRLog.e("Initialize error: \(error.localizedDescription)")
    } else {
        FRLog.i("Initialize succeeded")
    }
  }
import { PIProtect } from '@forgerock/ping-protect';

try {
  // Initialize PingOne Protect with manual configuration
  PIProtect.start({ envId: '3072206d-c6ce-4c19-a366-f87e972c7cc3' });
} catch (err) {
  console.error(err);
}

Initialize based on a callback

Not all authentication journeys perform risk evaluations, and therefore do not need to initialize data collection. You can choose to initialize capture of data on receipt of the PingOneProtectInitializeCallback callback rather than during app start up.

The callback also provides the configuration parameters.

  • Android

  • iOS

  • JavaScript

try {
  val callback =
    node.getCallback(PingOneProtectInitializeCallback::class.java)
  callback.start(context)
} catch (e: PingOneProtectInitException) {
  Logger.error("PingOneInitException", e, e.message)
} catch (e: Exception) {
  Logger.error("PingOneInitException", e, e.message)
  callback.setClientError(e.message);
}
node.next()
if callback.type == "PingOneProtectInitializeCallback",
  let pingOneProtectInitCallback = callback as? PingOneProtectInitializeCallback
{
  pingOneProtectInitCallback.start { result in
    DispatchQueue.main.async {
      var initResult = ""
      switch result {
      case .success:
        initResult = "Success"
      case .failure(let error):
        initResult = "Error: \(error.localizedDescription)"
      }
      FRLog.i("PingOne Protect Initialize Result: \n\(initResult)")
      handleNode(node)
    }
  }
  return
}
import { PIProtect } from '@forgerock/ping-protect';

if (step.getCallbacksOfType('PingOneProtectInitializeCallback')) {
  const callback = step.getCallbackOfType('PingOneProtectInitializeCallback');

  // Obtain config properties from the callback
  const config = callback.getConfig();

  console.log(JSON.stringify(config));

  try {
    // Initialize PingOne Protect with configuration from callback
    await PIProtect.start(config);
  } catch (err) {
    // Add any errors to the callback
    callback.setClientError(err.message);
  }
}

FRAuth.next(step);

Pause and resume behavioral data capture

The PingOne Protect Signals SDK can capture behavioral data, such as how the user interacts with the app, to help when performing evaluations.

There are scenarios where you might want to pause the collection of behavioral data. For example, the user might not be interacting with the app, or you only want to use device attribute data to be considered when performing PingOne Protect evaluations. You can then resume behavioral data collection when required.

The SDKs provide the pauseBehavioralData() and resumeBehavioralData() methods for pausing and resuming the capture of behavioral data.

The PingOneProtectEvaluationCallback callback can include a flag to pause or resume behavioral capture that you should respond to as follows:

  • Android

  • iOS

  • JavaScript

val callback =
  node.getCallback(PingOneProtectEvaluationCallback::class.java)

const shouldPause = callback.pauseBehavioralData

Logger.info("PingOneProtectEvaluationCallback", "getPauseBehavioralData: ${shouldPause}")

if (shouldPause) {
  PIProtect.pauseBehavioralData()
}
if callback.type == "PingOneProtectEvaluationCallback",
  let pingOneProtectEvaluationCallback = callback as? PingOneProtectEvaluationCallback
{
  if let shouldPause = pingOneProtectEvaluationCallback.pauseBehavioralData, shouldPause {
    PIProtect.pauseBehavioralData()
  }
}
const callback = step.getCallbackOfType('PingOneProtectEvaluationCallback');
const shouldPause = callback.getPauseBehavioralData();

console.log(`getPauseBehavioralData: ${shouldPause}`);

if (shouldPause) {
  PIProtect.pauseBehavioralData();
}

Return collected data for a risk evaluation

To perform risk evaluations, the PingOne server requires the captured data.

On receipt of a PingOneProtectEvaluationCallback callback, use the getData() method to populate the response with the captured data.

  • Android

  • iOS

  • JavaScript

try {
  val callback =
    node.getCallback(PingOneProtectEvaluationCallback::class.java)
  callback.getData(context)
} catch (e: PingOneProtectEvaluationException) {
  Logger.error("PingOneRiskEvaluationCallback", e, e.message)
} catch (e: Exception) {
  Logger.error("PingOneRiskEvaluationCallback", e, e.message)
}
if callback.type == "PingOneProtectEvaluationCallback",
  let pingOneProtectEvaluationCallback = callback as? PingOneProtectEvaluationCallback
{
  pingOneProtectEvaluationCallback.getData { result in
    DispatchQueue.main.async {
      var evaluationResult = ""
      switch result {
      case .success:
        evaluationResult = "Success"
      case .failure(let error):
        evaluationResult = "Error: \(error.localizedDescription)"
      }
      FRLog.i("PingOne Protect Evaluation Result: \n\(evaluationResult)")
      handleNode(node)
    }
  }
  return
}
let data;

if (step.getCallbacksOfType('PingOneProtectEvaluationCallback')) {
  const callback = step.getCallbackOfType('PingOneProtectEvaluationCallback');
  try {
    // Asynchronous call
    data = await PIProtect.getData();
  } catch (err) {
    // Add any errors to the callback
    callback.setClientError(err.message);
  }
}
callback.setData(data);
FRAuth.next(step);