ping-identity
    Preparing search index...

    Ping Identity

    Ping Identity React Native FIDO

    This package provides a native-backed FIDO bridge for React Native.

    Note: This module requires that the @ping-identity/rn-core module 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 install

    Optional integration packages:

    yarn add @ping-identity/rn-logger
    
    • Host Digital Asset Links at https://<rp-domain>/.well-known/assetlinks.json.
    • Ensure assetlinks.json contains 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.
    • 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.

    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: 'user@example.com',
    displayName: 'Example User',
    },
    pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
    });

    const authenticationResult = await fido.authenticate({
    challenge: 'base64url-challenge',
    rpId: 'example.com',
    allowCredentials: [],
    });

    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,
    });
    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: '...' });

    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({});
    }

    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.

    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);
    }
    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>;
    }

    Rejected promises throw a FidoError instance, which extends PingError extends Error. Use instanceof FidoError to narrow in catch blocks.

    Stable error codes:

    • FIDO_ERROR
    • FIDO_REGISTER_ERROR
    • FIDO_AUTHENTICATE_ERROR
    • FIDO_AUTHENTICATE_CANCELLED
    • FIDO_ACTIVITY_UNAVAILABLE (Android)
    • FIDO_WINDOW_UNAVAILABLE (iOS)
    • FIDO_CALLBACK_NOT_FOUND
    • FIDO_COLLECTOR_NOT_FOUND
    • android.useFido2Client is an Android-only override.
      • undefined (default): native SDK auto-detection/default behavior.
      • true: force Google Play Services FIDO2 APIs.
      • false: force Android Credential Manager APIs.
    • 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 FidoClient configuration.
    • Android requires a foreground Activity for FIDO calls.
    • iOS requires an active UIWindowScene/ASPresentationAnchor for FIDO calls.

    Full passkey E2E strategy (including OS-level credential surfaces outside app UI) is still to be determined.

    This project is licensed under the MIT License - see the LICENSE file for details