---
title: Step 4. Authenticating with external IdPs
description: Handle the IdpCollector with the useDaVinciForm hook and call authorizeForDaVinci to authenticate users with an external IdP in a DaVinci flow
component: orchsdks
page_id: orchsdks:davinci:use-cases/external-idp/react-native/04_authenticate_with_external_idps
canonical_url: https://developer.pingidentity.com/orchsdks/davinci/use-cases/external-idp/react-native/04_authenticate_with_external_idps.html
llms_txt: https://developer.pingidentity.com/orchsdks/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: Thu, 4 Sep 2026 00:00:00 +0000
keywords: ["PingOne", "DaVinci", "Flows", "Setup &amp; Configuration", "Source Code", "Integration", "SDK", "IdP", "React Native"]
section_ids:
  create-client: Creating a client
  rendering_the_social_sign_on_buttons: Rendering the social sign-on buttons
  authorize: Authorizing with the chosen provider
  errors: Error handling
---

# Step 4. Authenticating with external IdPs

[icon: circle-check, set=far]PingOne [icon: react, set=fab]React Native

With your providers configured, you're ready to wire up social sign-on in your React Native app.

When a user reaches the social sign-on part of a DaVinci flow, the flow returns a `ContinueNode` containing one `IdpCollector` for each external identity provider you configured in [Configuring social sign-on in PingOne](../before-you-begin.html).

To support this flow, your app needs to:

* Create an `ExternalIdpClient` instance.

* Render a sign-in button for each `IdpCollector` in the node.

* Call `authorizeForDaVinci` when the user chooses a provider, then advance the flow with `daVinci.next({ collectors: [] })`.

The sections below cover each step, followed by error handling.

## Creating a client

Create an `ExternalIdpClient` instance using the `createExternalIdpClient` function.

Provide a `redirectUri` that matches the URI scheme you registered in [Step 2. Handling URI schemes](02_handling_uri_schemes.html).

The client is reusable, so create it once and share it across your components, for example with a `useMemo` hook:

Creating an ExternalIdpClient instance

```typescript
import { createExternalIdpClient } from '@ping-identity/rn-external-idp';

const externalIdp = createExternalIdpClient({
  redirectUri: 'myapp://callback',
});

// The client is reusable. For example, inside a component:
// const externalIdp = useMemo(
//   () => createExternalIdpClient({ redirectUri: 'myapp://callback' }),
//   [],
// );
```

|   |                                                                                                                                                                                                |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | On Android, when you omit the `redirectUri`, the module falls back to the `appRedirectUriScheme` manifest placeholder you set in [Step 2. Handling URI schemes](02_handling_uri_schemes.html). |

## Rendering the social sign-on buttons

Render one sign-in button for each `IdpCollector` in the node, so users can choose how to authenticate:

![An app with three external IdP options; Apple, Facebook, and Google.](../../../_images/social-sign-on-example.png)Figure 1. An app with three external IdP options: Apple, Facebook, and Google.

The `useDaVinciForm` hook manages collector form state and submit planning for the active DaVinci node.

Create the form with the node from `useDaVinci`, and pass `handledCollectorTypes` so the social sign-on collectors are excluded from blocking submit issues:

Configuring useDaVinciForm to handle social sign-on collectors

```typescript
import { useDaVinci, useDaVinciForm } from '@ping-identity/rn-davinci';

const { node, next } = useDaVinci(daVinciClient);

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

Without `handledCollectorTypes`, each `SOCIAL_LOGIN_BUTTON` collector raises an `INTEGRATION_REQUIRED` issue and forces `form.canSubmit` to `false`, because the module treats these collectors as requiring client-side integration.

When you list `SOCIAL_LOGIN_BUTTON` in `handledCollectorTypes`, your app declares that it handles these collectors itself, and `form.canSubmit` reflects the readiness of the remaining form fields.

DaVinci flows represent each social sign-on option as an `IdpCollector` collector with the type `SOCIAL_LOGIN_BUTTON`.

> **Collapse: What fields are available in an IdpCollector?**
>
> The `IdpCollector` exposes the following fields:
>
> | Field        | Type                                                            | Description                                                                                                                  |
> | ------------ | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
> | `key`        | `string`                                                        | Stable server-assigned IdP identifier.Use this value as the form field key and as the React list key when rendering buttons. |
> | `type`       | `'SOCIAL_LOGIN_BUTTON'`                                         | The collector type.Always `SOCIAL_LOGIN_BUTTON` for social sign-on.                                                          |
> | `label`      | `string`                                                        | Human-readable button label, for example `Sign in with Google`.                                                              |
> | `idpId`      | `string`                                                        | Server IdP identifier.The same value as `key`.                                                                               |
> | `idpType`    | `'APPLE'`, `'FACEBOOK'`, `'GOOGLE'`, or another provider string | The provider type.Use this value to choose an icon or brand styling for the button.`APPLE` is available on iOS only.         |
> | `idpEnabled` | `boolean`                                                       | Whether the IdP is currently enabled.Disable the button when this is `false`.                                                |
> | `link`       | `string`, optional                                              | The IdP authentication URL.Informational only; your app does not use it directly.                                            |

Render one button for each social sign-on collector, using the `label` and `idpType` fields from each entry of `form.fields` to style it:

Rendering a social sign-on button for each IdpCollector

```typescript
const idpFields = form.fields.filter((field) => field.type === 'SOCIAL_LOGIN_BUTTON');

return idpFields.map((collector) => (
  <Button
    key={collector.key}
    title={collector.label}
    onPress={() => onIdpAuthorize(collector)}
    disabled={!collector.idpEnabled}
  />
));
```

## Authorizing with the chosen provider

When the user taps a social sign-on button, call `authorizeForDaVinci` with your `ExternalIdpClient` and the DaVinci client.

If the node contains more than one `IdpCollector`, pass the `index` of the collector the user chose:

Calling authorizeForDaVinci and advancing the flow

```typescript
import type { IdpCollector } from '@ping-identity/rn-davinci';

async function onIdpAuthorize(collector: IdpCollector): Promise<void> {
  const idpFields = form.fields.filter((f) => f.type === 'SOCIAL_LOGIN_BUTTON');
  const index = idpFields.findIndex((f) => f.key === collector.key);

  try {
    await externalIdp.authorizeForDaVinci(daVinciClient, { index });
    await next({ collectors: [] });
  } catch (error) {
    // Handle the error, see below
  }
}
```

|   |                                                                                                                                                                                                                                                                                                                                      |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | `authorizeForDaVinci` returns `void`, not a token.The token is carried into the subsequent `daVinci.next({ collectors: [] })` request by a request interceptor inside the native SDK.This is why the call to `next()` passes an empty `collectors` array. The flow advances on its own, with the token attached by the native layer. |

When `authorizeForDaVinci` resolves, the user has completed authentication with the external IdP, and the `next()` call advances the DaVinci flow to the next node.

## Error handling

All promise rejections from the **External IdP** module throw an `ExternalIdpError` instance.

Use `instanceof` to narrow the type:

Catching and inspecting ExternalIdpError

```typescript
import { ExternalIdpError } from '@ping-identity/rn-external-idp';

try {
  await externalIdp.authorizeForDaVinci(daVinciClient, { index: 0 });
  await next({ collectors: [] });
} catch (err) {
  if (err instanceof ExternalIdpError) {
    console.log(err.code, err.message);
  }
}
```

The error codes are:

* `EXTERNAL_IDP_AUTHORIZE_ERROR`

  The provider returned an error during authorization.

* `EXTERNAL_IDP_CANCELLED`

  The user cancelled the sign-in flow.

* `EXTERNAL_IDP_UNSUPPORTED_PROVIDER`

  The provider is not supported on this platform.

  In DaVinci flows the module falls back to a browser redirect when a native provider SDK is not linked, so this error is uncommon.

  The most common cause is a misconfigured native provider SDK, for example a Google client ID that does not match your app's package name or signing certificate.

* `EXTERNAL_IDP_CALLBACK_NOT_FOUND`

  The module could not find a matching `IdpCollector` in the DaVinci node.

* `EXTERNAL_IDP_CONFIG_ERROR`

  The client configuration is invalid.

* `EXTERNAL_IDP_ACTIVITY_UNAVAILABLE`

  (Android only) The Android activity required to launch the sign-in flow is unavailable.

* `EXTERNAL_IDP_WINDOW_UNAVAILABLE`

  (iOS only) The window required to present the sign-in flow is unavailable.
