This module exposes native-backed Journey clients for Android and iOS.
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-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;
}
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 <></>;
}
useJourneyFormimport { 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
SuspendedTextOutputCallbackdeep 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_ERRORJOURNEY_INIT_ERRORJOURNEY_START_ERRORJOURNEY_NEXT_ERRORJOURNEY_RESUME_ERRORJOURNEY_USER_ERRORJOURNEY_LOGOUT_ERRORJOURNEY_DISPOSE_ERRORJOURNEY_STATE_ERRORJOURNEY_CALLBACK_APPLY_ERRORJOURNEY_UNSUPPORTED_CALLBACK_ERRORJOURNEY_MISSING_INTEGRATION_ERROR