
The problem we set out to solve
Cross-platform development promises one codebase for both iOS and Android, but authentication is usually where that promise gets complicated. A production app needs more than a login screen. It has to handle token lifecycle, secure storage, biometrics, push, external identity providers, and multi-step orchestration, all from a single codebase.
The existing options usually forced a trade-off. Thin wrappers left the hard parts to the developer, browser-only flows limited how native the experience could feel, and custom bridges meant maintaining native code separately for both platforms. That’s why we’ve built our Ping Orchestration React Native SDK to close the gap: a TypeScript API that exposes the real capabilities of the underlying native SDKs without abstracting away the complexity you still need access to.
What’s in the toolbox
The SDK is a Yarn monorepo of independent packages. That means applications can install what they actually need instead of pulling in a single oversized package.
The SDK supports both Journey orchestration and standard OIDC flows. It handles the flow from the initial challenge through token handling, including redirect handling, PKCE, refresh, and session management. Those pieces are already built in, so the app doesn’t need to wire them up separately.
useJourney returns a [node, actions] tuple. node is a discriminated union for the current Journey state. Loading and error states live on actions. If the flow needs to be shared across screens, JourneyProvider can hold that state at the navigator level instead of passing it through props.
useJourneyForm is an optional hook for normalizing the callback array returned by a Journey step. It maps callbacks into typed fields with kind, prompt, and executionMode, covering regular inputs, background callbacks, and native handoffs like FIDO. That lets the UI decide whether to render an input, run work automatically, or delegate to a native capability. It also exposes canSubmit, issues, and a prebuilt form.input object that can be passed directly to actions.next().
MFA is part of the same model. Passkeys, TOTP, push MFA, and device binding are implemented as dedicated packages that plug into the flow rather than forcing a separate native project or a second bridge layer.
Token storage uses the native secure store on each platform. Device signals and device-bound features are separate packages, so apps can add them only if they need them.
The TypeScript surface is intentionally strict. Nodes, fields, config, and errors are typed unions, so application code can narrow correctly.
What the SDK covers is only part of the story. The other part is how those capabilities are exposed to application code.
How we designed the API
A big part of the work was making sure the API stayed predictable from the React Native side, while still exposing the native capabilities underneath.
Factory functions, not classes
Every client is created with a factory function that returns a plain object. There is no new, no inheritance, and no this. Configuration is validated at creation time, and the native instance initializes lazily on the first call. The result composes cleanly into a context, ref, or module-level singleton.
A consistent client contract
All authentication clients, such as OIDC and Journey, share the same surface: start, next, user, refresh, revoke, and dispose. Return types are discriminated throughout, so TypeScript narrows correctly at every call site.
React hooks, standalone or with a provider
useJourney and useOidc return a [state, actions] tuple. A client can be passed directly, or the argument can be omitted, and the hook will read from the nearest provider.
A screen can use a client directly when the flow is local:
const [state, actions] = useJourney(journeyClient)
Or the application can provide the client higher in the tree and let screens read it from context:
// Root component
<JourneyProvider client={journeyClient}>
<App />
</JourneyProvider>
The bridge: New Architecture and Classic, transparently
Every package supports both TurboModules and the classic bridge. The TypeScript layer resolves the correct native module at runtime, so application code does not need to handle either case. Moving an app to the New Architecture doesn’t require any changes to how the SDK is called.
In practice, that means the packages follow the same general patterns, while still allowing the app to work closer to native behavior when needed.
What developers can build with the SDK
The SDK is not prescriptive about your app’s structure. Because it covers the entire identity surface area, you have the flexibility to build:
- Passwordless sign-on: Implement passkeys and biometrics, with a Journey fallback for seamless enrollment or account recovery.
- Software TOTP: Enable users to enroll and generate verification codes directly inside the app instead of forcing them to switch to a separate authenticator.
- Push MFA approval flows: Design custom approval flows that match the app’s design system rather than dropping users into a generic web view.
- Device-bound sessions: Securely tie user credentials directly to a specific physical device.
- Social and external IdP handoff: Let the SDK manage the browser steps, token exchange, and resulting session states.
- Background device signal collection: Gather silent device signals as part of a Journey flow for real-time risk evaluation.
The following table maps those capabilities to the documented use cases and usage guides available today:
| Use case or guide | Description |
|---|---|
| FIDO and passkeys | Passwordless sign-on with passkeys and biometrics |
| Push notifications | Push-based MFA approval flows |
| OATH one-time passcodes | Software TOTP enrollment and verification |
| Social sign-on | External IdP handoff and token exchange |
| Device binding | Binding credentials to a specific physical device |
| Device profiling | Background device signal collection for risk evaluation |
| Device management | User-facing device self-service flows |
| User self-service | Profile management and account recovery |
| Magic links | Resuming journeys via deep-linked magic links |
| Journey orchestration | Configuring, starting, and navigating authentication journeys |
| SDK customization | Logging, storage, and network request customization |
How to get started
All packages are published to npm under the @ping-identity scope, so there’s nothing to configure beyond the standard registry.
The documented baseline targets React Native 0.80.1 and supports iOS 16.0+ and Android API 29+. For more information, refer to supported operating systems and browsers.
Install the packages the app needs. It is recommended to include the core package alongside the auth packages:
yarn add @ping-identity/rn-oidc @ping-identity/rn-journey @ping-identity/rn-core
ℹ️ Note: For teams evaluating the SDK, the sample app is one of the most useful starting points. The repository includes PingSampleApp, and the documentation positions it as the place where the SDK packages are exercised together. Follow our tutorial to get started!
What’s next
Version 1.0 targets teams building on PingOne Advanced Identity Cloud and PingAM — Journey orchestration, OIDC flows, token management, and MFA are all tested and supported against both platforms. If you’re running on either, this release is ready to use today.
The OIDC package also supports standard redirect-based flows against any OIDC-compliant authorization server, so teams not yet on a Ping Identity platform or that want an in-app browser redirect experience can use the React Native SDK.
Up next are:
-
React Native SDK agent skills: The goal is to make common setup work less repetitive: scaffold an authentication flow, wire Journey callbacks, configure OIDC, and generate a sample integration from a single prompt.
-
DaVinci support: The plan is to bring the same client and hook model used elsewhere in the SDK to PingOne DaVinci orchestration as that integration expands.

