ping-identity
    Preparing search index...

    Ping Identity

    Ping Identity React Native DaVinci

    This module provides native-backed PingOne DaVinci orchestration for React Native on Android and iOS. DaVinci is a flexible authentication and authorization library that drives server-defined flows through a simple node-based API. Your app calls start() to launch a flow, receives a node describing the current step, gathers the required input, and calls next() to advance — repeating until the flow reaches a terminal SuccessNode, ErrorNode, or FailureNode.

    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-davinci module
    yarn add @ping-identity/rn-davinci
    # If you are developing your app using iOS, run this command
    cd ios && pod install

    Optional integration packages:

    yarn add @ping-identity/rn-storage
    yarn add @ping-identity/rn-logger

    Use this baseline configuration first.

    import { createDaVinciClient } from '@ping-identity/rn-davinci';

    const client = createDaVinciClient({
    modules: {
    oidc: {
    clientId: 'rn-client',
    discoveryEndpoint:
    'https://auth.pingone.com/<env-id>/as/.well-known/openid-configuration',
    redirectUri: 'com.example.app://callback',
    },
    },
    });

    Add modules.oidc.storage when you need native-backed OIDC token persistence. Configure it only if you need persistent token storage; otherwise omit storage values.

    import { createDaVinciClient } from '@ping-identity/rn-davinci';
    import { CacheStrategy, configureOidcStorage } from '@ping-identity/rn-storage';

    const oidcStorage = configureOidcStorage({
    android: {
    fileName: 'davinci-oidc',
    keyAlias: 'davinci-oidc',
    strongBoxPreferred: true,
    cacheStrategy: CacheStrategy.CACHE_ON_FAILURE,
    },
    ios: {
    account: 'com.example.app.oidc',
    encryptor: true,
    cacheable: false,
    },
    });

    const client = createDaVinciClient({
    timeout: 30000,
    modules: {
    oidc: {
    clientId: 'rn-client',
    discoveryEndpoint:
    'https://auth.pingone.com/<env-id>/as/.well-known/openid-configuration',
    redirectUri: 'com.example.app://callback',
    scopes: ['openid', 'profile', 'email'],
    storage: oidcStorage,
    },
    },
    });

    Pass optional integrations through config.modules. The JS API is createDaVinciClient(config). When provided, the storage handle in modules.oidc.storage must come from configureOidcStorage(...).

    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 DaVinci config.

    import { createDaVinciClient } from '@ping-identity/rn-davinci';
    import { logger } from '@ping-identity/rn-logger';

    const jsLogger = logger({ level: 'debug' });

    const client = createDaVinciClient({
    logger: jsLogger,
    modules: {
    oidc: {
    clientId: 'rn-client',
    discoveryEndpoint:
    'https://auth.pingone.com/<env-id>/as/.well-known/openid-configuration',
    redirectUri: 'com.example.app://callback',
    scopes: ['openid'],
    },
    },
    });
    const firstNode = await client.start();
    const nextNode = await client.next({
    collectors: [
    { key: 'user', value: 'demo-user' },
    { key: 'password', value: 'demo-password' },
    ],
    });
    const session = await client.user();
    const refreshedSession = await client.refresh();
    const userInfo = await client.userinfo();
    await client.revoke();
    await client.logoutUser();
    await client.dispose();

    Handle node states explicitly in your UI flow:

    const node = await client.start();

    switch (node.type) {
    case 'ContinueNode':
    await client.next({
    collectors: [{ key: 'user', value: 'demo-user' }],
    });
    break;
    case 'ErrorNode':
    console.log(node.message);
    break;
    case 'FailureNode':
    console.log(node.cause ?? node.message);
    break;
    case 'SuccessNode':
    console.log('Authenticated — session:', node.session.value);
    break;
    }

    Post Authentication Operations

    After a DaVinci flow completes successfully, use the following operations to inspect and manage the active user session:

    const userSession = await client.user();

    if (userSession) {
    const refreshedSession = await client.refresh();
    const userInfo = await client.userinfo();
    await client.revoke();
    }

    await client.logoutUser();
    • user() returns token payload (accessToken, optional refreshToken, optional expiresIn).
    • refresh() refreshes token payload for the active user.
    • userinfo() fetches user claims for the active user.
    • revoke() revokes access/refresh tokens for the active user.
    • logoutUser() signs out and clears the active DaVinci user session.

    useDaVinci does not auto-advance nodes. Progression policy is app-controlled via explicit next(...) calls.

    import { useDaVinci } from '@ping-identity/rn-davinci';

    const { node, start, next, loading, error } = useDaVinci(client);

    await start();

    if (node?.type === 'ContinueNode') {
    await next({
    collectors: [{ key: 'user', value: 'demo-user' }],
    });
    }
    import { DaVinciProvider, useDaVinci } from '@ping-identity/rn-davinci';

    function App(): React.ReactElement {
    return (
    <DaVinciProvider config={config}>
    <AuthNavigator />
    </DaVinciProvider>
    );
    }

    function LoginScreen(): React.ReactElement {
    const { node, start, next, loading } = useDaVinci();

    if (!node) {
    return <Button title="Sign In" onPress={start} />;
    }

    if (node.type === 'SuccessNode') {
    return <Text>Authenticated</Text>;
    }

    if (node.type === 'ContinueNode') {
    return <DaVinciForm node={node} onNext={next} loading={loading} />;
    }

    return <Text>{node.message}</Text>;
    }
    import { useDaVinci, useDaVinciForm } from '@ping-identity/rn-davinci';

    const { node, next } = useDaVinci(client);
    const form = useDaVinciForm(node);

    form.setValueByType('TEXT', 'demo-user');
    form.setValueByType('PASSWORD', 'demo-password');

    if (form.canSubmit) {
    await next(form.input);
    }

    useDaVinciForm is headless. It manages normalized collectors and submit planning, but does not render UI and does not auto-run collectors.

    Each normalized collector includes executionMode and requiresUserInput.

    executionMode Meaning requiresUserInput default
    manual Collector value is submitted from form/planned input. true
    immediate Activating the collector immediately advances the flow without waiting for other fields. false
    output_only Display/label collector, no input value expected. false
    integration_required Collector is handled by an external integration package before submit. false
    unsupported Collector type is not currently handled by the bridge or any registered integration. false

    FLOW_BUTTON, FLOW_LINK, and ACTION collectors bypass other form fields and immediately advance the flow. Use submitFlow(key) inside a DaVinciProvider tree:

    const form = useDaVinciForm(node);

    // Activates "Forgot Password" flow link directly — no other field values are included.
    await form.submitFlow('forgot-password');

    The following collector types are supported on Android and iOS:

    Collector Type Description Input Handling
    TEXT Single-line text input. Manual input
    PASSWORD Masked password input. Manual input
    PASSWORD_VERIFY Password-confirmation variant of PASSWORD. Manual input
    SINGLE_SELECT Single-select input. Manual input
    DROPDOWN Single-select dropdown. Manual input
    RADIO Single-select radio group. Manual input
    MULTI_SELECT Multi-select input. Manual input
    COMBOBOX Multi-select combobox. Manual input
    CHECKBOX Multi-select checkbox group. Manual input
    PHONE_NUMBER Phone number input with country code. Manual input
    DEVICE_REGISTRATION Device picker for registration. Manual input
    DEVICE_AUTHENTICATION Device picker for authentication. Manual input
    SUBMIT_BUTTON Triggers form submission immediately. Immediate
    ACTION Action button that advances the flow immediately. Immediate
    FLOW_BUTTON Flow button that advances the flow immediately. Immediate
    FLOW_LINK Flow link that advances the flow immediately. Immediate
    SINGLE_CHECKBOX Single checkbox or toggle (boolean field). Manual input
    LABEL Read-only display content. Output-only
    READ_ONLY_TEXT Read-only text / agreement content. Output-only
    POLLING Async polling collector — see Polling and QR code flows. Output-only
    QR_CODE Display-only QR code — see Polling and QR code flows. Output-only
    FIDO2 FIDO passkey registration or authentication; narrow by action. Integration

    Integration-dependent collectors are surfaced in node payloads with executionMode: 'integration_required'. Their minimum generic shape is key, type, and optional raw; the owning integration package may provide additional fields and the operation needed before next(). Use the integration package's exported collector type/constant and pass its type in handledCollectorTypes:

    import { socialLoginCollectorType } from '@ping-identity/rn-external-idp';

    const form = useDaVinciForm(node, {
    handledCollectorTypes: new Set([socialLoginCollectorType]),
    });

    Known collector-specific fields should be accessed only after narrowing with the owning package's type or helper. Generic integration collectors do not guarantee fields such as label, options, or value.

    FIDO2 is owned by @ping-identity/rn-fido. It is one collector type, with action: 'REGISTER' for publicKeyCredentialCreationOptions or action: 'AUTHENTICATE' for publicKeyCredentialRequestOptions. Create a FIDO client before the node is normalized so it registers FIDO2 with the integration registry, and include the type in handledCollectorTypes:

    import {
    createFidoClient,
    fidoCollectorType,
    type FidoCollector,
    } from '@ping-identity/rn-fido';

    const fido = createFidoClient();
    const form = useDaVinciForm(node, {
    handledCollectorTypes: new Set([fidoCollectorType]),
    });

    for (const collector of node.collectors) {
    const fidoCollector = collector as FidoCollector;
    if (fidoCollector.action === 'REGISTER') {
    await fido.registerForDaVinci(daVinci, { index: 0 });
    } else if (fidoCollector.action === 'AUTHENTICATE') {
    await fido.authenticateForDaVinci(daVinci, { index: 0 });
    }
    }

    // The native collector retains the ceremony result for submission.
    await daVinci.next({ collectors: [] });

    The ceremony must be completed before calling next, and the FIDO collector key must not be passed in next input. These DaVinci methods use native ceremony defaults; no React Native ceremony customization options are exposed.

    When the native SDK cannot instantiate a collector from the server payload, the bridge surfaces it in ContinueNode.unsupportedFields:

    if (node.type === 'ContinueNode' && node.unsupportedFields?.length) {
    console.warn('Unsupported fields present:', node.unsupportedFields);
    }

    Each entry has key and type so the UI can render a placeholder or block submission.

    All promise rejections throw a DaVinciError instance, which extends PingError extends Error. Use instanceof to narrow the error type:

    import { DaVinciError } from '@ping-identity/rn-davinci';

    try {
    await client.start();
    } catch (err) {
    if (err instanceof DaVinciError) {
    console.log(err.code, err.type, err.message);
    }
    }

    Stable DaVinci error codes:

    • DAVINCI_CONFIG_ERROR
    • DAVINCI_START_ERROR
    • DAVINCI_NEXT_ERROR
    • DAVINCI_COLLECTOR_APPLY_ERROR
    • DAVINCI_SESSION_ERROR
    • DAVINCI_LOGOUT_ERROR
    • DAVINCI_DISPOSE_ERROR
    • DAVINCI_POLL_ERROR
    • DAVINCI_ARGUMENT_ERROR
    • DAVINCI_STATE_ERROR
    • DAVINCI_MISSING_INTEGRATION_ERROR
    • DAVINCI_UNKNOWN_ERROR

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