Ping SDKs

Step 3. Develop the client app

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

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

Initialize data collection

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

Your client application can manually initialize data collection, and must provide the configuration to control the PingOne Signals SDK.

There are three main methods for initializing data collection:

Direct initialization using the Protect interface

The DaVinci Client allows you to initialize data collection directly using the Protect interface. This provides maximum flexibility in how the collection operates.

To directly initialize data collection using the Protect interface, complete these steps:

  1. Add a configuration object to your code that defines the property values for data collection.

    The available properties are as follows:

    • Android

    • iOS

    • JavaScript

    Parameter Description

    envID

    Required. Your PingOne environment identifier.

    For example, 3072206d-c6ce-ch15-m0nd-f87e972c7cc3

    deviceAttributesToIgnore

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

    For example, AUDIO_OUTPUT_DEVICES or IS_ACCEPT_COOKIES.

    isBehavioralDataCollection

    When true, collect behavioral data.

    Default is true.

    isConsoleLogEnabled

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

    Default is false.

    isLazyMetadata

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

    Default is false.

    customHost

    Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.

    Parameter Description

    envID

    Required. Your PingOne environment identifier.

    For example, 3072206d-c6ce-ch15-m0nd-f87e972c7cc3

    deviceAttributesToIgnore

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

    For example, AUDIO_OUTPUT_DEVICES or IS_ACCEPT_COOKIES.

    isBehavioralDataCollection

    When true, collect behavioral data.

    Default is true.

    isConsoleLogEnabled

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

    Default is false.

    isLazyMetadata

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

    Default is false.

    customHost

    Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.

    Parameter Description

    envID

    Required. Your PingOne environment identifier.

    For example, 3072206d-c6ce-ch15-m0nd-f87e972c7cc3

    deviceAttributesToIgnore

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

    For example, AUDIO_OUTPUT_DEVICES or IS_ACCEPT_COOKIES.

    behavioralDataCollection

    When true, collect behavioral data.

    Default is true.

    hubUrl

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

    disableHub

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

    When false the client uses an iframe.

    Default is false.

    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.

    externalIdentifiers

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

    waitForWindowLoad

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

    Default is true.

    universalDeviceIdentification

    Optional. When true, device data in the payload returned to the server is provided as a signed JWT.

    Example code:

    • Android

    • iOS

    • JavaScript

    Protect.config {
        isBehavioralDataCollection = true
        isLazyMetadata = true
        envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        deviceAttributesToIgnore = listOf("deviceId", "androidId", "serialNumber")
        isConsoleLogEnabled = true
    }
    await Protect.config { protectConfig in
        protectConfig.isBehavioralDataCollection = true
        protectConfig.isLazyMetadata = true
        protectConfig.envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        protectConfig.deviceAttributesToIgnore = ["deviceId", "serialNumber"]
        protectConfig.isConsoleLogEnabled = true
    }
    const protectApi = protect({
        behavioralDataCollection: true,
        envId: "3072206d-c6ce-ch15-m0nd-f87e972c7cc3",
        deviceAttributesToIgnore: ['deviceId', 'serialNumber'],
    });
  2. Call an initialize function to start the data collection using the configuration object:

    • Android

    • iOS

    • JavaScript

    Protect.init()
    
    Log.d("Protect", "Protect data collection initialized.")
    try await Protect.initialize()
    
    print("Protect data collection initialized.")
    await protectApi.start();
    
    console.log('Protect data collection initialized.');

Use the ProtectLifecycle module for Android or iOS

The DaVinci Client for Android and iOS provide the ProtectLifecycle module for simplifying the management of data collection.

As a DaVinci Client module, it is aware of the current state of authentication, and can automatically pause and resume behavioral data collection when required.

Configure the ProtectLifecycle module in your DaVinci Client configuration, as with other modules.

The available properties are as follows:

Parameter Description

envID

Required. Your PingOne environment identifier.

For example, 3072206d-c6ce-ch15-m0nd-f87e972c7cc3

deviceAttributesToIgnore

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

For example, AUDIO_OUTPUT_DEVICES or IS_ACCEPT_COOKIES.

isBehavioralDataCollection

When true, collect behavioral data.

Default is true.

isConsoleLogEnabled

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

Default is false.

isLazyMetadata

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

Default is false.

customHost

Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios.

pauseBehavioralDataOnSuccess

When true, the DaVinci Client automatically pauses behavioral data collection after successful authentication.

Default is false.

resumeBehavioralDataOnStart

When true, the DaVinci Client automatically starts behavioral data collection when it initializes.

Example code:

  • Android

  • iOS

DaVinci {
    timeout = 30
    module(Oidc) {
        clientId = "dummy"
        // ... Other OIDC configuration
    }
    module(ProtectLifecycle) {
        isBehavioralDataCollection = true
        isLazyMetadata = true
        envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        deviceAttributesToIgnore = listOf("deviceId")
        isConsoleLogEnabled = true

        pauseBehavioralDataOnSuccess = true
        resumeBehavioralDataOnStart = true
    }
}
let davinci = DaVinci.createDaVinci { config in
    config.module(OidcModule.config) { oidcValue in
        oidcValue.clientId = "dummy"
        // ... Other OIDC configuration
    }
    config.module(ProtectLifecycleModule.config) { protectValue in
        protectValue.isBehavioralDataCollection = true
        protectValue.isLazyMetadata = true
        protectValue.envId = "3072206d-c6ce-ch15-m0nd-f87e972c7cc3"
        protectValue.deviceAttributesToIgnore = ["deviceId"]
        protectValue.isConsoleLogEnabled = true

        protectValue.pauseBehavioralDataOnSuccess = true
        protectValue.resumeBehavioralDataOnStart = true
    }
}

On-demand initialization on receipt of ProtectCollector

You can choose not to initialize data collection on app startup and instead initialize it on-demand, when your DaVinci flow reaches the relevant node.

The DaVinci for Android and iOS will automatically initialize data collection when calling the collect() method in response to receiving a ProtectCollector from PingOne. Learn more in Return captured data to a DaVinci flow.

In the DaVinci Client for JavaScript, you can use the start() method to initialize data collection in response to receiving a ProtectCollector from PingOne:

JavaScript example of on-demand initialization
const collectors = davinciClient.getCollectors();
collectors.forEach((collector) => {
  if (collector.type === 'ProtectCollector') {
    // Optionally use configuration options from the flow to initialize the protect module
    const config = collector.output.config;

    // Initialize the Protect module and begin collecting data
    const protectApi = protect({
      envId: '3072206d-c6ce-ch15-m0nd-f87e972c7cc3',
      behavioralDataCollection: config.behavioralDataCollection,
      universalDeviceIdentification: config.universalDeviceIdentification,
    });
    await protectApi.start();
  }
});

Use the collector.output.config object to obtain the properties for initializing data collection as configured by the DaVinci flow.

Pause and resume behavioral data capture

Part of the data collection includes collecting behavioral data, such as how the user interacts with the app, to help when performing evaluations.

There are scenarios where you might want to manually 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 manually pausing and resuming the capture of behavioral data:

  • Android

  • iOS

  • JavaScript

Protect.pauseBehavioralData() // Pause data collection.
Protect.resumeBehavioralData() // Resume data collection.
await Protect.pauseBehavioralData() // Pause data collection.
await Protect.resumeBehavioralData() // Resume data collection.
await protectApi.pauseBehavioralData() // Pause data collection.
await protectApi.resumeBehavioralData() // Resume data collection.

Return captured data to a DaVinci flow

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

On receipt of ProtectCollector your app should gather the collected data, update the collector, and call next() to return the collected data to PingOne:

  • Android

  • iOS

  • JavaScript

On receipt of ProtectCollector, use the collect() method to populate the response with the captured data.

When the data is successfully collected, call node.next() to submit the data back to the server for evaluation.

node.collectors.forEach {
    when (it) {
        is ProtectCollector -> {
            when (val result = it.collect()) {
                is Success -> {
                    // Data collection successful: Proceed to the next node in the DaVinci flow.
                    node.next()
                }
                is Failure -> {
                    // Data collection failed: Implement robust error handling.
                    // Example: Log the error, display an informative message, or implement a retry mechanism.
                }
            }
        }
        // ... Handle other collector types
    }
}

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

When the data is successfully collected, call node.next() to submit the data back to the server for evaluation.

node.collectors.forEach { collector in
    switch collector {
    case let protectCollector as ProtectCollector:
        let result = await protectCollector.collect()
        switch result {
        case .success:
            // Data collection successful: Proceed to the next node in the DaVinci flow.
            node.next()
        case .failure:
            // Data collection failed: Implement robust error handling.
            // Example: Log the error, display an informative message, or implement a retry mechanism.
            break
        }
    // ... Handle other collector types
    default:
        break
    }
}

On receipt of a ProtectCollector callback, use the getData() method to prepare the collected data, and call the update() method populate the response with that captured data.

When the data is successfully collected, call next() to submit the data back to the server for evaluation.

async function onSubmitHandler() {
  try {
    const protectCollector = collectors.find((collector) => collector.type === 'ProtectCollector');

    // Update the Protect collector with the data collected
    if (protectCollector) {
      const updater = davinciClient.update(protectCollector);
      const data = await protectApi.getData();
      updater(data);
    }

    // Submit all collectors and get the next node in the flow
    await davinciClient.next();
  } catch (err) {
    // handle error
  }
}