---
title: Step 4. Authenticating with external IdPs
description: Wire up external IdP authentication in React Native by handling SelectIdpCallback and IdpCallback with the ExternalIdpClient module
component: orchsdks
page_id: orchsdks:journey:use-cases/external-idp/react-native/04_authenticate_with_external_idps
canonical_url: https://developer.pingidentity.com/orchsdks/journey/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: Tue, 10 Jun 2026 00:00:00 +0000
keywords: ["PingOne Advanced Identity Cloud", "PingAM", "Setup &amp; Configuration", "Source Code", "Integration", "SDK", "React Native"]
section_ids:
  creating_a_client: Creating a client
  handling_callbacks: Handling callbacks
  selectIdp: Handling SelectIdpCallback
  idp: Handling IdpCallback
  full_example: Full example
  error_handling: Error handling
---

# Step 4. Authenticating with external IdPs

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

With your providers configured, you're ready to wire up the authentication flow in your React Native app.

When a user reaches the social sign-on part of a journey, they see a list of the identity providers you've enabled, such as Apple, Facebook, and Google.

After they choose a provider, the module handles authentication. This uses the provider's native SDK where available, or a browser redirect where native SDK support is not available on that platform.

To support this flow, your app needs to:

* Create an `ExternalIdpClient` instance.

* Handle `SelectIdpCallback` to render the provider list and record the user's selection.

* Handle `IdpCallback` to trigger the native sign-in flow for the chosen provider.

The sections below cover each step, followed by a full example and error handling reference.

## Creating a client

Create an `ExternalIdpClient` instance using `createExternalIdpClient`.

Provide a `redirectUri` for devices that don't support a native browser session (ASWebAuthenticationSession on iOS, Custom Tabs on Android), where the external IdP falls back to a redirect flow instead.

The value must match the URI scheme you registered in [Step 2. Handling URI schemes](02_handling_uri_schemes.html):

Creating an ExternalIdpClient instance

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

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

  // Add optional logger
  logger: logger({ level: 'debug' }),
});
```

## Handling callbacks

A journey that supports external IdPs will usually return two callback types in sequence:

1. [SelectIdpCallback](#selectIdp)

2. [IdpCallback](#idp)

### Handling SelectIdpCallback

When the journey encounters a [Select Identity Provider node](https://docs.pingidentity.com/auth-node-ref/latest/select-identity-provider.html), the client receives `SelectIdpCallback`. This callback lists the identity providers that are configured in your PingOne Advanced Identity Cloud tenant or PingAM server.

Within the `SelectIdpCallback` is a `providers` array. Each entry has a `provider` identifier string and an optional `uiConfig` object with display metadata.

Read the array to render a provider picker, then call `selectProviderForJourney` with the `journey` instance and the chosen `provider` value, followed by `journey.next({})`:

Handling SelectIdpCallback to render and select a provider

```typescript
import type { JourneyInstance } from '@ping-identity/rn-types';
import type { JourneyNormalizedField, JourneyNode } from '@ping-identity/rn-journey';
import { useJourneyForm } from '@ping-identity/rn-journey';

type Provider = {
  provider: string;
  uiConfig?: {
    buttonDisplayName?: string;
    buttonImage?: string;
  };
};

function getProviders(field: JourneyNormalizedField): Provider[] {
  return (field.raw as { providers?: Provider[] }).providers ?? [];
}

function ProviderPicker({ journey, node }: { journey: JourneyInstance, node: JourneyNode }) {
  const { fields } = useJourneyForm(node);

  const selectIdpField = fields.find(f => f.ref.type === 'SelectIdpCallback');
  const providers = selectIdpField ? getProviders(selectIdpField) : [];

  // Render the identity providers, for example as buttons in your UI

  async function onProviderChosen(selected: string) {
    await externalIdp.selectProviderForJourney(journey, selected);
    await journey.next({});
  }
}
```

The `provider` value is the identifier string as configured in your Advanced Identity Cloud tenant or PingAM server. For example, `'google'`, `'facebook'`, or `'apple'`.

The `journey` parameter is the `JourneyInstance` returned by `useJourney`.

|   |                                                                                                                                                                                                                    |
| - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   | If a journey node contains more than one `SelectIdpCallback`, use the `index` option to target the correct callback:```typescript
await externalIdp.selectProviderForJourney(journey, selected, { index: 1 });
``` |

### Handling IdpCallback

When an authentication journey encounters a [Social Provider Handler node](https://docs.pingidentity.com/auth-node-ref/latest/social-provider-handler.html) your client receives an `IdpCallback`.

On receipt of the `IdpCallback`, call `authorizeForJourney`.

The module handles the authentication flow — whether through a native SDK or a browser redirect — and resolves the promise when complete. No deep-link listener or `journey.resume()` call is needed.

Call `journey.next({})` after the promise resolves:

Calling authorizeForJourney and advancing the journey

```typescript
await externalIdp.authorizeForJourney(journey);
await journey.next({});
```

|   |                                                                                                                                                                                                                      |
| - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | If a journey node contains more than one `SelectIdpCallback` or `IdpCallback`, use the `index` option to target the correct callback:```typescript
await externalIdp.authorizeForJourney(journey, { index: 1 });
``` |

## Full example

Handling external IdP callbacks end to end

```typescript
import { createExternalIdpClient } from '@ping-identity/rn-external-idp';
import type { JourneyInstance, NodeCallback } from '@ping-identity/rn-types';

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

async function handleExternalIdpNode(
  journey: JourneyInstance,
  callbacks: NodeCallback[],
): Promise<void> {
  for (const cb of callbacks) {
    if (cb.type === 'SelectIdpCallback') {
      const provider = getUserSelectedProvider(); // e.g. 'google'
      await externalIdp.selectProviderForJourney(journey, provider);
    }

    if (cb.type === 'IdpCallback') {
      await externalIdp.authorizeForJourney(journey);
    }
  }

  await journey.next({});
}
```

## 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.authorizeForJourney(journey);
} 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.

  This can occur when the corresponding native SDK is absent, or when a browser redirect flow is required but no valid `redirectUri` was provided.

* `EXTERNAL_IDP_CALLBACK_NOT_FOUND`

  The module could not find a matching callback in the journey 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.
