npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ping-identity/rn-davinci

v1.1.0-beta.0

Published

Ping Identity DaVinci orchestration for React Native

Readme

Ping Identity

Ping Identity React Native DaVinci

Overview

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.

Integrating the SDK into your project

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

How to Use the SDK

Step 1: Create a minimal client

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',
      scopes: ['openid', 'profile'],
    },
  },
});

Step 2: Add optional storage

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.

Set modules.oidc.par to true to enable the Pushed Authorization Request flow. The native SDK reads the PAR endpoint from the provider's OIDC discovery document.

Warning: If the provider's discovery document does not advertise a PAR endpoint, the native SDK sends the authorization request to an empty URL instead of failing fast. DaVinci's OIDC config does not expose an endpoint override, so confirm PAR support in your provider's discovery document before enabling par: true.

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'],
      par: true,
      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(...).

Step 3: Add logging integration (optional)

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

Drive the DaVinci flow imperatively

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

Approving a device authorization grant

When this device is acting as the approving device in an RFC 8628 device authorization grant, pass the verification_uri_complete URL from the device authorization response to start(). The DaVinci flow extracts the user_code from that URL and approves the requesting device.

The useDaVinci hook exposes the option through its start action (see Use the React hook).

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.

Use the React hook

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

To approve an RFC 8628 device authorization grant, pass the verificationUri option to start (see Approving a device authorization grant):

await start({
  verificationUri: 'https://example.com/device?user_code=WDJB-MJHT',
});

After the DaVinci flow authenticates the user, the native SDK extracts the user_code from that URL and approves the requesting device automatically; no extra submit step is required in the app.

Share DaVinci state across multiple screens (optional)

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

Render collectors with useDaVinciForm

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.

Validate a collector without advancing the flow

Use validate(collectorKey, value) to validate one active collector, such as when a field loses focus. It returns that collector's validation errors without calling next():

const { validate } = useDaVinci(client);

const errors = await validate('email', 'not-an-email');

if (errors.length > 0) {
  // Example: [{ code: 'REGEX_ERROR', message: 'Invalid email' }]
}

validate() applies value to the active native collector before checking it. The value remains on the collector and is included in a later next() call. An empty array means the value has no validation errors or that the collector has no native validator.

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 collector submission

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

Core collector support

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.

Unsupported fields

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.

Error handling

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_VALIDATE_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

License

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