Integrating FIDO DaVinci flows into React Native apps
PingOne React Native
The FIDO module provides native-backed FIDO capabilities for React Native apps, including registering and authenticating with passkeys.
|
Before you begin
Ensure you have prepared PingOne for FIDO authentication in React Native apps before completing the steps on this page. |
Step 1. Installing modules
The FIDO module for React Native is available as the @ping-identity/rn-fido npm package.
The module requires the @ping-identity/rn-core package. Install both packages in your React Native project:
-
yarn
-
npm
yarn add @ping-identity/rn-core @ping-identity/rn-fido
npm install @ping-identity/rn-core @ping-identity/rn-fido
Installing native dependencies on iOS
After installing the package, install the native iOS dependencies using CocoaPods:
cd ios && pod install
Adding optional dependencies on Android
Gradle resolves the Android native dependencies automatically.
Optionally, add the following dependencies to your app’s build.gradle if needed:
-
If your app targets Android API 33 (Android 13) or older, add
androidx.credentials:credentials-play-services-authto ensure FIDO functions correctly on those devices. -
If your app needs to support non-discoverable FIDO credentials or physical security keys, add
com.google.android.gms:play-services-fido.
Step 2. Creating the FIDO client
Create a FIDO client by calling createFidoClient().
Create the client once and reuse it for the lifetime of the relevant screen or session, for example with a useMemo hook:
import { createFidoClient } from '@ping-identity/rn-fido';
const fidoClient = createFidoClient();
You can call createFidoClient() without arguments.
The client does not require configuration for the DaVinci ceremony methods, registerForDaVinci() and authenticateForDaVinci(). These methods build their own native FIDO client and use the logger configured in the DaVinci workflow, so client-level options such as logger have no effect on them.
Step 3. Handling FIDO2 collectors in a DaVinci flow
When a DaVinci flow reaches a FIDO step, the flow returns a ContinueNode containing a FIDO2 collector.
DaVinci flows represent each FIDO operation as a single FIDO2 collector, and the collector’s action property tells you which operation it performs:
REGISTER-
A registration collector. The user is registering a new passkey with the flow.
AUTHENTICATE-
An authentication collector. The user is authenticating with an existing passkey.
To support this flow, your app needs to:
-
Create the form with the node from
useDaVinci, and passFIDO2inhandledCollectorTypes. -
Render a passkey button for each FIDO2 collector in the node.
-
Call
registerForDaVinciorauthenticateForDaVincidepending on the collector’saction, then advance the flow withdaVinci.next({ collectors: [] }).
Configuring useDaVinciForm to handle FIDO collectors
The useDaVinciForm hook manages form state for the collectors on the active DaVinci node, and plans what the app submits when the form is complete.
Create the form with the node from useDaVinci, and pass the FIDO2 collector type in handledCollectorTypes so the FIDO collectors are excluded from blocking submit issues:
import { useDaVinci, useDaVinciForm } from '@ping-identity/rn-davinci';
import { fidoCollectorType } from '@ping-identity/rn-fido';
const { node, next } = useDaVinci(daVinciClient);
const form = useDaVinciForm(node, {
handledCollectorTypes: new Set([fidoCollectorType]),
});
Without handledCollectorTypes, each FIDO2 collector raises an INTEGRATION_REQUIRED issue and forces form.canSubmit to false, because the module treats these collectors as requiring client-side integration.
When you list FIDO2 in handledCollectorTypes, your app declares that it handles these collectors itself, and form.canSubmit reflects the readiness of the remaining form fields.
What fields are available in a FIDO2 collector?
The FIDO collector exposes the following fields:
| Field | Type | Description |
|---|---|---|
|
|
Stable server-assigned identifier used as the collector key. |
|
|
The collector type. Always |
|
|
The ceremony action. Use this value to decide whether to call |
|
|
Human-readable label rendered for the collector. |
|
|
Whether the collector must be completed before submission. |
|
|
Android-only trigger string surfaced by the native collector. The iOS collector does not expose this field. |
|
|
WebAuthn public key credential creation options returned by the server, on registration collectors. |
|
|
WebAuthn public key credential request options returned by the server, on authentication collectors. |
|
|
Original server-side field JSON when available. |
Step 4. Registering FIDO authenticators
To register a FIDO authenticator, call registerForDaVinci() on the FIDO client with the active DaVinci client.
The method runs the native platform passkey ceremony, and the native layer submits the resulting attestation to the flow before the method returns:
async function handleFidoCeremony(collector) {
if (collector.action === 'REGISTER') {
await fidoClient.registerForDaVinci(daVinciClient, { index });
await next({ collectors: [] });
}
}
The daVinciClient parameter is a DaVinci client created with createDaVinciClient(). Learn more in Configuring the DaVinci module in React Native.
If the node contains more than one FIDO2 registration collector, pass the zero-based index of the collector the user chose.
|
The attestation payload returned by The native layer submits the credential to the DaVinci flow itself, which is why the call to |
Step 5. Authenticating using a FIDO authenticator
To authenticate using a registered FIDO authenticator, call authenticateForDaVinci() on the FIDO client.
The method runs the native platform passkey ceremony, and the native layer submits the resulting assertion to the flow before the method returns:
async function handleFidoCeremony(collector) {
if (collector.action === 'AUTHENTICATE') {
await fidoClient.authenticateForDaVinci(daVinciClient, { index });
await next({ collectors: [] });
}
}
If the node contains more than one FIDO2 authentication collector, pass the zero-based index of the collector the user chose.
Because both ceremony methods submit their result through the native layer, a common pattern is to branch on the collector’s action in a single handler and advance the flow after the ceremony resolves:
import type { FidoCollector } from '@ping-identity/rn-fido';
async function handleFidoCeremony(collector: FidoCollector): Promise<void> {
const fidoFields = form.fields.filter(
(f) => f.type === fidoCollectorType &&
(f as FidoCollector).action === collector.action,
);
const index = fidoFields.findIndex((f) => f.key === collector.key);
try {
if (collector.action === 'REGISTER') {
await fidoClient.registerForDaVinci(daVinciClient, { index });
} else {
await fidoClient.authenticateForDaVinci(daVinciClient, { index });
}
await next({ collectors: [] });
} catch (err) {
// Handle the error, see below
}
}
|
When a node contains both registration and authentication collectors, compute the |
Step 6. Handling cancellation and errors
When a FIDO operation fails or is cancelled, the SDK throws a FidoError.
Use the code property to distinguish cancellation from other failures:
import { FidoError } from '@ping-identity/rn-fido';
try {
await handleFidoCeremony(collector);
} catch (err) {
if (err instanceof FidoError) {
if (err.code === 'FIDO_AUTHENTICATE_CANCELLED') {
// Non-fatal. On Android this also fires when the device has no
// enrolled passkey (NoCredentialException), in which case routing
// the user to registration may be more appropriate than offering
// a fallback authentication path.
return;
}
// For other errors, fall through and advance the flow
// so the server can route to its configured failure branch
}
await next({ collectors: [] });
}
Error codes
| Error code | Description | ||
|---|---|---|---|
|
A general FIDO error that does not fall into a more specific category. |
||
|
Registration failed. The platform reported an error creating the credential. |
||
|
Authentication failed due to a platform or server-side error. |
||
|
The FIDO authentication ceremony ended without an assertion. This can be caused by any of the following:
You can treat this as non-fatal and offer a fallback authentication path. |
||
|
The Android Activity required to launch the FIDO UI is unavailable. |
||
|
The iOS window required to present the FIDO UI is unavailable. |
||
|
The module could not find a matching |