Step 2. Integrating PingOne Protect DaVinci flows in React Native
PingOne React Native
Integrating your application with PingOne Protect enables you to perform risk evaluations during your customer’s authentication flow.
The PingOne Protect module runs silently in the background; it requires no user interaction.
Initializing PingOne Protect data collection
The earlier you can initialize data collection, the more data it can collect to make a risk evaluation.
There are two main methods for initializing data collection in a React Native app:
Manual initialization
Initialize the PingOne Protect module by calling startProtect() with a configuration object:
import { startProtect } from '@ping-identity/rn-protect';
await startProtect({
envId: "3072206d-c6ce-ch15-m0nd-f87e972c7cc3",
isBehavioralDataCollection: true,
resumeBehavioralDataOnStart: true,
});
The available configuration properties are as follows:
| Parameter | Description | ||
|---|---|---|---|
|
Required. Your PingOne environment identifier. For example, |
||
|
Optional. A list of device attributes to ignore when collecting device signals. For example, |
||
|
When Default is |
||
|
When Default is |
||
|
When Default is |
||
|
Optional. Specify a custom host for the Protect API, which can be useful in specific deployment scenarios. |
||
|
When Default is
|
||
|
When |
|
Call When If you create the client first, the client does not include the lifecycle module, and those properties have no effect. |
On-demand initialization
You can choose not to initialize data collection on app startup and instead initialize it when your DaVinci flow reaches a risk evaluation step.
Deferring initialization means your app only collects behavioral data in flows that actually perform PingOne Protect risk evaluations, because behavioral data collection runs continuously once initialized.
|
On-demand initialization is incompatible with the If you initialize on demand, pause and resume behavioral data collection manually with the |
When your DaVinci flow returns a ContinueNode containing a PROTECT collector, detect it using the protectCollectorType constant, initialize the module, and collect the data.
In the following example, daVinciClient is a DaVinci client created with createDaVinciClient(), as described in Configuring the DaVinci module in React Native, and node is the active flow node:
import {
startProtect,
collectProtect,
protectCollectorType,
} from '@ping-identity/rn-protect';
// When a ContinueNode arrives:
if (node.collectors.some((c) => c.type === protectCollectorType)) {
// Initialize the Protect module
await startProtect({
envId: '3072206d-c6ce-ch15-m0nd-f87e972c7cc3',
isBehavioralDataCollection: true,
});
// Collect Protect data for the active PROTECT collector
await collectProtect(daVinciClient);
// Submit all collectors and get the next node in the flow
await daVinciClient.next({});
}
|
The native layer does apply one setting from the DaVinci flow during When the flow disables behavioral data collection, the native SDK pauses it before gathering the risk data. |
Pausing and resuming behavioral data capture
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 pause the collection of behavioral data:
-
To reduce memory and processor utilization. Continuously collecting behavioral data beyond authentication could reduce the performance of your client app.
-
When you only want to consider device attribute data when performing PingOne Protect evaluations.
You can pause, and also resume behavioral data collection as required.
The PingOne Protect module provides the pauseBehavioralData() and resumeBehavioralData() methods for manually pausing and resuming the capture of behavioral data:
import { pauseBehavioralData, resumeBehavioralData } from '@ping-identity/rn-protect';
await pauseBehavioralData(); // Pause data collection.
await resumeBehavioralData(); // Resume data collection.
Call pauseBehavioralData() after a successful authentication flow, and resumeBehavioralData() at the start of a new flow.
If you initialize the PingOne Protect module before you create your DaVinci client, and the DaVinci flows you use complete with a success node, the pauseBehavioralDataOnSuccess property pauses behavioral data collection for you. Manual pausing is then only needed if you want to pause at other points.
There are some differences in behavior to be aware of between Android and iOS:
- Android
-
-
Calling either
pauseBehavioralData()orresumeBehavioralData()before you have initialized PingOne resolves silently on Android. -
Android returns
PROTECT_INITIALIZE_ERRORon failures.
-
- iOS
-
-
Calling either
pauseBehavioralData()orresumeBehavioralData()before you have initialized PingOne rejects on iOS. -
iOS returns
PROTECT_COLLECT_ERRORon failures.
-
Both methods also accept an optional logger property, so you can output log messages from the pause and resume operations:
import { pauseBehavioralData, resumeBehavioralData } from '@ping-identity/rn-protect';
import { logger } from '@ping-identity/rn-logger';
const protectLogger = logger({ level: 'debug' });
await pauseBehavioralData({ logger: protectLogger });
await resumeBehavioralData({ logger: protectLogger });
You can pass the same logger instance to the PingOne Protect module itself through the logger property in the startProtect() configuration.
Returning data to a DaVinci flow for risk evaluation
To perform risk evaluations, the PingOne server requires the captured data.
When your DaVinci flow reaches a PingOne Protect step, the flow returns a ContinueNode containing a PROTECT collector.
Call collectProtect() with your DaVinci client before you advance the flow. The method runs data collection and sets the resulting payload on the native collector, so that the subsequent next() call submits it automatically.
If you use the useDaVinci and useDaVinciForm hooks to manage form state, as described in Stepping through DaVinci flows in React Native, collect the Protect data as soon as the node arrives, then submit the form as normal:
import { useEffect } from 'react';
import { useDaVinci, useDaVinciForm } from '@ping-identity/rn-davinci';
import { collectProtect, protectCollectorType } from '@ping-identity/rn-protect';
const { node, next } = useDaVinci(daVinciClient);
const form = useDaVinciForm(node, {
handledCollectorTypes: new Set([protectCollectorType]), (1)
});
// Run Protect data collection as soon as a node with a PROTECT collector arrives
useEffect(() => {
if (node?.type !== 'ContinueNode') return;
const hasProtectCollector = node.collectors.some(
(c) => c.type === protectCollectorType,
);
if (!hasProtectCollector) return;
collectProtect(daVinciClient).catch(console.error); (2)
}, [node]);
async function handleSubmit() {
// The Protect payload has already been collected by the useEffect above
if (form.canSubmit) {
await next(form.input); (3)
}
}
The example above does three things:
-
Passes
protectCollectorTypeinhandledCollectorTypes. 1Without this, each
PROTECTcollector raises anINTEGRATION_REQUIREDissue and forcesform.canSubmittofalse, because the module treats these collectors as requiring client-side integration. -
Runs
collectProtect()in auseEffectwhen the node arrives, rather than waiting for submission. 2By the time the user submits, the flow has already collected the Protect payload, and the
next(form.input)call forwards it. -
Submits the form with
next(form.input)guarded byform.canSubmit. 3By doing this the submit reflects the readiness of the remaining form fields. The native layer attaches the Protect payload when the flow advances, so
form.inputneeds no Protect value from your app.
If you do not use the hooks, you can call collectProtect() directly before you advance the flow:
import { collectProtect } from '@ping-identity/rn-protect';
async function onSubmitHandler() {
try {
// Run Protect data collection for the active PROTECT collector
await collectProtect(daVinciClient);
// Submit all collectors and get the next node in the flow
await daVinciClient.next({});
} catch (err) {
// Handle the error, see below
}
}
Handling errors
All promise rejections from the Protect module throw a ProtectError instance.
When you collect data in a useEffect as shown earlier, handle the rejection there, for example with .catch(). Handling the rejection in the effect ensures a collection failure does not go unnoticed if the user still submits the form.
Use instanceof to narrow the error type:
import { ProtectError } from '@ping-identity/rn-protect';
try {
await collectProtect(daVinciClient);
await daVinciClient.next({});
} catch (err) {
if (err instanceof ProtectError) {
console.log(err.code, err.message);
}
}
The error codes are:
PROTECT_INITIALIZE_ERROR-
The native SDK failed to initialize, pause, or resume data collection.
PROTECT_COLLECT_ERROR-
The native SDK failed to collect risk signals.
PROTECT_COLLECTOR_NOT_FOUND-
The current DaVinci node does not contain an active
PROTECTcollector.