---
title: Step 2. Authenticating with external IdPs
description: PingOne JavaScript
component: orchsdks
page_id: orchsdks:davinci:use-cases/external-idp/javascript/02_authenticate_with_external_idps
canonical_url: https://developer.pingidentity.com/orchsdks/davinci/use-cases/external-idp/javascript/02_authenticate_with_external_idps.html
revdate: Tue, 25 Mar 2025 11:00:37 +0100
keywords: ["DaVinci", "Flows", "Tutorial", "Source Code", "Integration", "SDK", "Android"]
section_ids:
  dv-flow: Handling external IdP nodes in DaVinci flows
---

# Step 2. Authenticating with external IdPs

[icon: circle-check, set=far]PingOne [icon: js, set=fab]JavaScript

Your app must handle the relevant node types your server returns when a user attempts to authenticate using an external IdP.

A DaVinci flow returns an `IdpCollector` to the client, which contains details about the external IdP the user chose for authentication.

## Handling external IdP nodes in DaVinci flows

When encountering a `IdpCollector` collector in a DaVinci flow, use the `externalIdp()` method to obtain the details from the collector:

Calling externalIdp to authenticate users with an external IdP

```typescript
const collectors = davinciClient.getCollectors();

collectors.forEach((collector) => {
  if (collector.type === 'IdpCollector') {
    socialLoginButtonComponent(formEl, collector, davinciClient.externalIdp());
  }
}
```

In this example, a `socialLoginButtonComponent` handles rendering the button and redirecting the user to the selected identity provider:

Example `social-login-button.ts` file to render social sign-on buttons

```typescript
import type { IdpCollector } from "@forgerock/davinci-client/types";

export default function socialLoginButtonComponent(
  formEl: HTMLFormElement,
  collector: IdpCollector,
  updater: () => void
) {
  const button = document.createElement("button");

  button.value = collector.output.label;
  button.innerHTML = collector.output.label;

  if (collector.output.label.toLowerCase().includes('google')) {
    button.style.background = 'white'
    button.style.borderColor = 'grey'
  } else if (collector.output.label.toLowerCase().includes('facebook')) {
    button.style.color = 'white'
    button.style.background = 'royalblue'
    button.style.borderColor = 'royalblue'
  } else if (collector.output.label.toLowerCase().includes('apple')) {
    button.style.color = 'white'
    button.style.background = 'black'
    button.style.borderColor = 'black'
  }

  button.onclick = async () => {
    await updater();
    window.location.assign(collector.output.url);
  };

  formEl?.appendChild(button);
}
```

After authenticating with the external IdP, they return to PingOne. PingOne generates a `continueToken` and attaches it as a query parameter to the URL that redirects back to the JavaScript client.

You must configure your JavaScript app to continue a flow on receipt of the `continueToken`.

Use the `resume()` method to continue an existing flow, rather than start a new one:

Continuing an existing DaVinci flow by using a `continueToken`

```typescript
const davinciClient = await davinci({ config });
const urlParams = new URLSearchParams(window.location.search);
const continueToken = urlParams.get('continueToken');
let resumeNode;

if (continueToken) {
  // Continue an existing flow
  resumeNode = await davinciClient.resume({ continueToken });
} else {
  // Setup configuration for a new flow
  await Config.setAsync(config);
}
```
