@ping-identity/rn-fido
v1.1.0
Published
The FIDO Module provides native-backed FIDO capabilities for React Native.
Readme
Ping Identity React Native FIDO
This package provides a native-backed FIDO bridge for React Native.
Table of contents
- Install
- FIDO prerequisites
- Client-first usage
- Journey integration
- DaVinci integration
useJourneyFormintegration- API reference
- Errors
- Platform notes
- License
Install
Note: This module requires that the
@ping-identity/rn-coremodule is already set up and installed.
# Install & setup the core module
yarn add @ping-identity/rn-core
# Install the rn-fido module
yarn add @ping-identity/rn-fido
# If you are developing your app using iOS, run this command
cd ios && pod installOptional integration packages:
yarn add @ping-identity/rn-loggerFIDO prerequisites
Android
- Host Digital Asset Links at
https://<rp-domain>/.well-known/assetlinks.json. - Ensure
assetlinks.jsoncontains your Android package name and signing cert fingerprint(s). - Configure Android origin domains for FIDO on your RP/server.
- If you support Android API 33 and older, explicitly include
androidx.credentials:credentials-play-services-auth. - If you need non-discoverable credentials / legacy security-key support, explicitly include
com.google.android.gms:play-services-fido.
iOS
- Enable Associated Domains entitlement.
- Add associated domain entries (for example
webcredentials:<rp-domain>). - Host Apple App Site Association at
https://<rp-domain>/.well-known/apple-app-site-association. - Ensure AASA contains your Apple Team ID and Bundle ID mapping.
Client-first usage
Use createFidoClient(config?) and call operations on the returned client.
import { createFidoClient } from '@ping-identity/rn-fido';
import { logger } from '@ping-identity/rn-logger';
const log = logger({ level: 'debug' });
const fido = createFidoClient({
logger: log,
android: {
useFido2Client: true,
},
});
const registrationResult = await fido.register({
challenge: 'base64url-challenge',
rp: { id: 'example.com', name: 'Example Inc.' },
user: {
id: 'base64url-user-id',
name: '[email protected]',
displayName: 'Example User',
},
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
});
const authenticationResult = await fido.authenticate({
challenge: 'base64url-challenge',
rpId: 'example.com',
allowCredentials: [],
});Logging integration (optional)
If you install the logger package, pass a JS logger instance created via
@ping-identity/rn-logger.
If the logger package is not installed/configured, do not pass logger values in FIDO config.
JavaScript-side FIDO logs use this logger on both platforms.
Native logger forwarding applies to standalone operations on both platforms.
Journey and DaVinci collector ceremonies retain their workflow-configured native logger.
import { createFidoClient } from '@ping-identity/rn-fido';
import { logger } from '@ping-identity/rn-logger';
const jsLogger = logger({ level: 'debug' });
const fido = createFidoClient({
logger: jsLogger,
});Multi-client example
import { createFidoClient } from '@ping-identity/rn-fido';
const fidoA = createFidoClient({
android: { useFido2Client: true },
});
const fidoB = createFidoClient({
android: { useFido2Client: false },
});
await fidoA.register({ challenge: '...' });
await fidoB.authenticate({ challenge: '...' });Journey integration
Run Journey FIDO callbacks explicitly before journey.next(...).
import { createFidoClient } from '@ping-identity/rn-fido';
const fido = createFidoClient();
if (node.type === 'ContinueNode') {
for (const callback of node.callbacks ?? []) {
if (callback.type === 'FidoRegistrationCallback') {
await fido.registerForJourney(journey, {
index: 0,
deviceName: 'My Device',
});
}
if (callback.type === 'FidoAuthenticationCallback') {
await fido.authenticateForJourney(journey, { index: 0 });
}
}
await journey.next({});
}DaVinci integration
FIDO2 DaVinci collectors use one collector type and an action discriminator:
action: 'REGISTER' exposes creation options, while action: 'AUTHENTICATE'
exposes request options. Create a FIDO client before starting or normalizing
the DaVinci flow — createFidoClient registers fidoCollectorType
('FIDO2') with the integration registry and eagerly registers the native
collector serializer, so nodes mapped before the first ceremony still carry
action and the WebAuthn options payload.
Run the ceremony, then advance the DaVinci flow with an empty collector input:
import { useDaVinciForm } from '@ping-identity/rn-davinci';
import {
createFidoClient,
fidoCollectorType,
type FidoCollector,
} from '@ping-identity/rn-fido';
const fido = createFidoClient();
const form = useDaVinciForm(node, {
handledCollectorTypes: new Set([fidoCollectorType]),
});
const collector = node.collectors[0] as FidoCollector;
if (collector.action === 'REGISTER') {
await fido.registerForDaVinci(daVinci, { index: 0 });
} else {
await fido.authenticateForDaVinci(daVinci, { index: 0 });
}
// The native collector retains the attestation or assertion for submission.
await daVinci.next({ collectors: [] });When using useDaVinciForm, include fidoCollectorType in
handledCollectorTypes after creating the FIDO client. Do not pass the FIDO
collector key to next(). The initial DaVinci integration uses native ceremony
defaults and does not expose React Native customization options.
The optional index selects among multiple FIDO2 collectors with the same
action. The methods return the native attestation or assertion payload for
informational use; the native collector submits it when the flow advances.
useJourneyForm integration
When using useJourneyForm, pass handledCallbackTypes so FIDO fields are excluded from
blocking submit issues. Run each integration, then submit when form.canSubmit is true.
import { useJourney, useJourneyForm } from '@ping-identity/rn-journey';
import { createFidoClient } from '@ping-identity/rn-fido';
import { nativeExtensionCallbackType } from '@ping-identity/rn-types';
const [node, actions] = useJourney(client);
const form = useJourneyForm(node, {
handledCallbackTypes: new Set([
nativeExtensionCallbackType.FidoRegistrationCallback,
nativeExtensionCallbackType.FidoAuthenticationCallback,
]),
});
const fido = createFidoClient();
for (const field of form.fields) {
if (field.ref.type === nativeExtensionCallbackType.FidoRegistrationCallback) {
await fido.registerForJourney(journey, { index: field.ref.typeIndex });
}
if (
field.ref.type === nativeExtensionCallbackType.FidoAuthenticationCallback
) {
await fido.authenticateForJourney(journey, { index: field.ref.typeIndex });
}
}
if (form.canSubmit) {
await actions.next(form.input);
}API reference
import { createFidoClient } from '@ping-identity/rn-fido';
import type {
FidoClient,
FidoConfig,
FidoRegistrationOptions,
FidoRegistrationResult,
FidoAuthenticationOptions,
FidoAuthenticationResult,
FidoJourneyRegistrationOptions,
FidoJourneyAuthenticationOptions,
FidoJourneyResult,
FidoDaVinciRegistrationOptions,
FidoDaVinciAuthenticationOptions,
FidoDaVinciResult,
DaVinciInstance,
JourneyInstance,
} from '@ping-identity/rn-fido';
function createFidoClient(config?: FidoConfig): FidoClient;
interface FidoClient {
register(options: FidoRegistrationOptions): Promise<FidoRegistrationResult>;
authenticate(
options: FidoAuthenticationOptions,
): Promise<FidoAuthenticationResult>;
registerForJourney(
journey: JourneyInstance,
options?: FidoJourneyRegistrationOptions,
): Promise<FidoJourneyResult>;
authenticateForJourney(
journey: JourneyInstance,
options?: FidoJourneyAuthenticationOptions,
): Promise<FidoJourneyResult>;
registerForDaVinci(
daVinci: DaVinciInstance,
options?: FidoDaVinciRegistrationOptions,
): Promise<FidoDaVinciResult>;
authenticateForDaVinci(
daVinci: DaVinciInstance,
options?: FidoDaVinciAuthenticationOptions,
): Promise<FidoDaVinciResult>;
}Errors
Rejected promises throw a FidoError instance, which extends PingError extends Error. Use instanceof FidoError to narrow in catch blocks.
Stable error codes:
FIDO_ERRORFIDO_REGISTER_ERRORFIDO_AUTHENTICATE_ERRORFIDO_AUTHENTICATE_CANCELLEDFIDO_ACTIVITY_UNAVAILABLE(Android)FIDO_WINDOW_UNAVAILABLE(iOS)FIDO_CALLBACK_NOT_FOUNDFIDO_COLLECTOR_NOT_FOUND
Platform notes
android.useFido2Clientis an Android-only override for the standaloneregister/authenticateoperations.undefined(default): native SDK auto-detection/default behavior.true: force Google Play Services FIDO2 APIs.false: force Android Credential Manager APIs.
- Journey and DaVinci ceremony methods (
registerForJourney,authenticateForJourney,registerForDaVinci,authenticateForDaVinci) currently ignore the client-level config on both platforms: ceremonies keep the workflow-configured native logger and native client defaults, soandroid.useFido2Clientand the client-level logger id have no effect there. - iOS accepts the same config shape for API parity, but does not currently apply native client-level config.
- Journey callback execution currently follows native SDK behavior; Android Journey callback APIs do not currently accept injected custom native
FidoClientconfiguration. - Android requires a foreground
Activityfor FIDO calls. - iOS requires an active
UIWindowScene/ASPresentationAnchorfor FIDO calls.
E2E testing note
Full passkey E2E strategy (including OS-level credential surfaces outside app UI) is still to be determined.
License
This project is licensed under the MIT License - see the LICENSE file for details
