ping-identity
    Preparing search index...

    Ping Identity

    Ping Identity React Native Journey

    This module exposes native-backed Journey clients for Android and iOS.

    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-journey module
    yarn add @ping-identity/rn-journey
    # 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 { createJourneyClient } from '@ping-identity/rn-journey';

    const client = createJourneyClient({
    serverUrl: 'https://example.com/am',
    realm: 'alpha',
    cookie: 'iPlanetDirectoryPro',
    timeout: 30000,
    });

    Add modules.oidc when your Journey flow needs OIDC token/session operations (for example user(), refresh(), userinfo(), and revoke()).

    Storage inside modules.oidc.storage and modules.session.storage is optional. Configure it only if you need native-backed persistence; otherwise omit storage values.

    import { createJourneyClient } from '@ping-identity/rn-journey';
    import {
    CacheStrategy,
    configureOidcStorage,
    configureSessionStorage,
    } from '@ping-identity/rn-storage';

    const sessionStorage = configureSessionStorage({
    android: {
    fileName: 'journey-session',
    keyAlias: 'journey-session',
    strongBoxPreferred: true,
    cacheStrategy: 'cache_on_failure',
    },
    ios: {
    account: 'com.example.app.session',
    encryptor: true,
    cacheable: false,
    },
    });

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

    const client = createJourneyClient({
    serverUrl: 'https://example.com/am',
    realm: 'alpha',
    cookie: 'iPlanetDirectoryPro',
    timeout: 30000,
    modules: {
    oidc: {
    clientId: 'rn-client',
    discoveryEndpoint:
    'https://example.com/am/oauth2/alpha/.well-known/openid-configuration',
    redirectUri: 'com.example.app://callback',
    scopes: ['openid', 'profile', 'email'],
    storage: oidcStorage,
    },
    session: {
    storage: sessionStorage,
    },
    },
    });

    Pass optional integrations through config.modules. The JS API is createJourneyClient(config). When provided, storage handles in modules.session.storage and modules.oidc.storage must come from configureSessionStorage(...) / 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 Journey config.

    import { createJourneyClient } from '@ping-identity/rn-journey';
    import { logger } from '@ping-identity/rn-logger';

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

    const client = createJourneyClient({
    serverUrl: 'https://example.com/am',
    logger: jsLogger,
    modules: {
    oidc: {
    clientId: 'rn-client',
    discoveryEndpoint:
    'https://example.com/am/oauth2/alpha/.well-known/openid-configuration',
    redirectUri: 'com.example.app://callback',
    scopes: ['openid'],
    },
    },
    });
    const firstNode = await client.start('Login');
    const nextNode = await client.next({
    callbacks: [
    { type: 'NameCallback', value: 'demo-user' },
    { type: 'PasswordCallback', value: 'demo-password' },
    ],
    });
    const resumedNode = await client.resume('com.example.app://callback?code=123');
    const session = await client.user();
    const refreshedSession = await client.refresh();
    const userInfo = await client.userinfo();
    const ssoToken = await client.ssoToken();
    await client.revoke();
    await client.logoutUser();
    await client.dispose();

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

    Start options are supported when initiating a journey:

    const node = await client.start('Login', {
    forceAuth: true,
    noSession: true,
    });

    Handle node states explicitly in your UI flow:

    const node = await client.start('Login');

    switch (node.type) {
    case 'ContinueNode':
    await client.next({
    callbacks: [{ type: 'NameCallback', 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');
    break;
    }

    Post Authentication Operations

    After a Journey login succeeds, use the following operations to inspect and manage the active user session:

    const userSession = await client.user();
    const ssoToken = await client.ssoToken();

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

    await client.logoutUser();
    • user() returns token/session payload (accessToken, optional refreshToken, expiresIn, optional userInfo).
    • ssoToken() returns Journey SSO session payload (value, successUrl, realm) when available.
    • 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 Journey user session.
    import { useJourney } from '@ping-identity/rn-journey';

    const [node, actions] = useJourney(client);

    await actions.start('Login');

    if (node?.type === 'ContinueNode') {
    await actions.next({
    callbacks: [{ type: 'NameCallback', value: 'demo-user' }],
    });
    }
    import { JourneyProvider, useJourney } from '@ping-identity/rn-journey';

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

    function LoginScreen(): React.ReactElement {
    const [node, actions] = useJourney();
    return <></>;
    }
    import { callbackType } from '@ping-identity/rn-types';
    import { useJourney, useJourneyForm } from '@ping-identity/rn-journey';

    const [node, actions] = useJourney(client);
    const form = useJourneyForm(node);

    const usernameField = form.getFieldByType(callbackType.NameCallback);
    const passwordField = form.getFieldByType(callbackType.PasswordCallback);

    form.setValueByType(callbackType.NameCallback, 'demo-user');
    form.setValueByType(callbackType.PasswordCallback, 'demo-password');

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

    useJourneyForm is headless. It manages normalized fields and submit planning, but does not render UI and does not auto-run callbacks.

    Each normalized field includes executionMode and requiresUserInput.

    executionMode Meaning requiresUserInput default
    manual Callback value is submitted from form/planned input. true
    auto_capable Callback can be executed by app auto-progress policy. false
    integration_required Callback family needs extra native/app integration before submit. false
    output_only Display/system callback, no input value expected. false
    unsupported Callback is not currently handled by helper submit logic. false

    requiresUserInput exceptions:

    Callback Type executionMode requiresUserInput Reason
    HiddenValueCallback manual false Hidden payload should pass through submit planning without forcing a visible input step.

    TODO(test-runner app): add Journey integration and E2E tests (including SuspendedTextOutputCallback deep link/email resume flow) once the test-runner app is set up.

    The following AM core callbacks are supported on Android and iOS:

    Callback Type Description Input Handling
    BooleanAttributeInputCallback Collects true or false input. Manual input
    ChoiceCallback Collects a single selection from available choices. Manual input
    ConfirmationCallback Collects one selected option from a list. Manual input
    ConsentMappingCallback Prompts the user to consent to sharing profile data. Manual input
    HiddenValueCallback Carries non-visual form values. Manual input
    KbaCreateCallback Collects knowledge-based question and answer values. Manual input
    MetadataCallback Injects key-value metadata into the flow. Output-only
    NameCallback Collects a username. Manual input
    NumberAttributeInputCallback Collects a numeric value. Manual input
    PasswordCallback Collects a password or OTP value. Manual input
    PollingWaitCallback Instructs the client to wait and resubmit later. Output-only
    StringAttributeInputCallback Collects string attribute values. Manual input
    SuspendedTextOutputCallback Pauses flow and resumes through external callback/deep link. Output-only
    TermsAndConditionsCallback Collects acceptance of configured terms and conditions. Manual input
    TextInputCallback Collects arbitrary text input. Manual input
    TextOutputCallback Provides display-only message content. Output-only
    ValidatedCreatePasswordCallback Collects password input with policy validation. Manual input
    ValidatedCreateUsernameCallback Collects username input with policy validation. Manual input

    Integration-dependent families (for example, device profile, FIDO, PingOne Protect, redirect/IdP, and ReCaptcha callbacks) are surfaced in node payloads and require client-side integration before submission (executionMode: 'integration_required').

    For FIDO callbacks, run explicit Journey-scoped APIs from @ping-identity/rn-fido (registerForJourney(...) / authenticateForJourney(...)) and then call next(...).

    Journey FIDO callback handling is standardized across Android and iOS with the following native contract:

    Area Standardized behavior
    Callback type resolution Runtime native callback class is authoritative; raw callback JSON type is fallback only.
    Callback execution ownership FIDO callback execution is owned by @ping-identity/rn-fido Journey-scoped APIs, not next(...) callback mutations.
    Hidden callback ownership Journey/native callback execution remains the source of truth for callback state; helper layer does not force custom override values.

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

    import { JourneyError } from '@ping-identity/rn-journey';

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

    Stable Journey error codes:

    • JOURNEY_CONFIG_ERROR
    • JOURNEY_INIT_ERROR
    • JOURNEY_START_ERROR
    • JOURNEY_NEXT_ERROR
    • JOURNEY_RESUME_ERROR
    • JOURNEY_USER_ERROR
    • JOURNEY_LOGOUT_ERROR
    • JOURNEY_DISPOSE_ERROR
    • JOURNEY_STATE_ERROR
    • JOURNEY_CALLBACK_APPLY_ERROR
    • JOURNEY_UNSUPPORTED_CALLBACK_ERROR
    • JOURNEY_MISSING_INTEGRATION_ERROR