Ping SDKs

Ping SDKs with DaVinci Flows

Introducing the DaVinci client for DaVinci flows

Server support:

  • PingOne

  • PingOne Advanced Identity Cloud

  • PingAM

  • PingFederate

SDK support:

  • Ping SDK for Android

  • Ping SDK for iOS

  • Ping SDK for JavaScript

The DaVinci clients provide powerful orchestration capabilities with PingOne DaVinci. They enable you to consume DaVinci flows to meet your use cases, all while providing a native Android or iOS, or a single-page app JavaScript experience.

You have complete control of your UI, so you can create the tailored experience you desire for your end users, all while leaving the DaVinci client to do the heavy lifting of communication between your app and your DaVinci flows.

UI Development

You are in charge of the experience your end users have in your Android, iOS, or JavaScript (SPA) applications.

Theme and brand your app the way you want to, focusing on your application logic and letting the DaVinci client communicate with PingOne DaVinci.

Dynamically Updating of DaVinci Flows

The DaVinci client works with PingOne DaVinci server-driven orchestration. This means that as long as you don’t hardcode UI elements in your application, and you appropriately handle the collector types, then you can update your DaVinci flow without needing to update your app or redeploy to the app stores.

Your app responds to changes in your DaVinci flows, without redeploying.
Figure 1. Your app responds to changes in your DaVinci flows, without redeploying.

This saves time and enables you to control your application orchestration experience without unnecessary burden.

Flow collectors

DaVinci sends requests to the DaVinci client from UI-related connectors. This enables you to step through each piece of information, and gather information from your end users appropriately.

Step through flows and and collect input from your users.
Figure 2. Step through flows and and collect input from your users.

For example, if you created a DaVinci flow to register a user, and you used the HTTP Connector - Custom HTML Template capability:

  • First name, last name, and email address would be sent as the TextCollector type to your app, and you would determine the way in which you represented this field in your UI.

  • The password field would be sent as the PasswordCollector type.

  • Once the flow completes, PingOne sends back the necessary tokens, and the DaVinci client automatically stores, retrieves, and refreshes tokens as needed.

Token Management

The DaVinci client uses the OAuth 2.0 auth code flow, and support PKCE.

This method is the best practice for first-party applications. The SDK automatically handles token exchange for you, and also securely stores the tokens.

The DaVinci client handles token refresh automatically, so you don’t have to think about it.

Configure the DaVinci client

The DaVinci clients are designed to be flexible and can be customized to suit many different situations.

Learn more about configuring and customizing the DaVinci clients in the following sections:

Configure DaVinci client properties

Learn how to configure DaVinci client properties so they can connect to your PingOne instance and step through DaVinci flows.

Default DaVinci client headers

Learn about the default headers the DaVinci clients add to requests to help you track and audit transactions or alter a DaVinci flow based on the values.

Configure DaVinci client properties

Applies to:

  • DaVinci client for Android

  • DaVinci client for iOS

  • DaVinci client for JavaScript

You need to configure certain settings so that the DaVinci client can connect to your PingOne instance to step through your DaVinci flows and authenticate your users.

The method you use to configure these settings depends on which platform you are using.

Configure DaVinci client for Android properties

Applies to:

  • DaVinci client for Android

  • DaVinci client for iOS

  • DaVinci client for JavaScript

Configure DaVinci client for Android properties to connect to PingOne and step through an associated DaVinci flow.

Installing and importing the DaVinci client

To use the DaVinci client for Android, add the relevant dependencies to your project:

  1. In the Project tree view of your Android Studio project, open the Gradle Scripts/build.gradle.kts file for the DaVinci module.

  2. In the dependencies section, add the following:

    implementation("com.pingidentity.sdks:davinci:1.0.0")
    gradle
    Example of the dependencies section after editing:
    dependencies {
    
        val composeBom = platform(libs.androidx.compose.bom)
        implementation(composeBom)
    
        // DaVinci client
        implementation("com.pingidentity.sdks:davinci:1.0.0")
    
        ...
    
        implementation(libs.androidx.core.ktx)
        implementation(libs.androidx.appcompat)
        implementation(libs.material)
    }
    gradle

Configuring connection properties

Create an instance of the DaVinci object and use the underlying Oidc module to set configuration properties.

The following properties are available for configuring the DaVinci client for Android:

Properties
Property Description Required?

discoveryEndpoint

Your PingOne server’s .well-known/openid-configuration endpoint.

Example:

https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration

Yes

clientId

The client_id of the OAuth 2.0 client profile to use.

For example, 6c7eb89a-66e9-ab12-cd34-eeaf795650b2

Yes

scopes

A set of scopes to request when performing an OAuth 2.0 authorization flow.

For example, "openid", "profile", "email", "address", "revoke".

Yes

redirectUri

The redirect_uri as configured in the OAuth 2.0 client profile.

This value must match a value configured in your OAuth 2.0 client.

For example, .

Yes

timeout

A timeout, in seconds, for each request that communicates with the server.

Default is 30 seconds.

No

acrValues

Request which flow the PingOne server uses by adding an Authentication Context Class Reference (ACR) parameter.

Enter a single DaVinci policy by using its flow policy ID.

Example:

"d1210a6b0b2665dbaa5b652221badba2"

No

Example

The following shows an example DaVinci client configuration, using the underlying Oidc module:

Configure DaVinci client connection properties
import com.pingidentity.davinci.DaVinci
import com.pingidentity.davinci.module.Oidc

val daVinci = DaVinci {
    module(Oidc) {
        clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"
        discoveryEndpoint = "https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/" +
            "as/.well-known/openid-configuration"
        scopes = mutableSetOf("openid", "profile", "email", "address", "revoke")
        redirectUri = ""
    }
}
kotlin

Stepping through DaVinci flows

To authenticate your users the DaVinci client for Android must start the flow, and step through each node.

Starting a DaVinci flow

To start a DaVinci flow, call the start() method:

Start a DaVinci flow
val node = daVinci.start()
kotlin

Determining DaVinci flow node type

Each step of the flow returns one of four node types:

ContinueNode

This type indicates there is input required from the client. The node object for this type contains a collector object, which describes the information it requires from the client.

SuccessNode

This type indicates the flow is complete, and authentication was successful.

ErrorNode

This type indicates an error in the data sent to the server. For example, an email address in an incorrect format, or a password that does not meet complexity requirements.

You can correct the error and resubmit to continue the flow.

FailureNode

This type indicates that the flow could not be completed and must be restarted. This can be caused by a server error, or a timeout.

You can use the helper functions to determine which node type the server has returned:

Determine node type.
when (node) {
    is ContinueNode -> {}
    is ErrorNode -> {}
    is FailureNode -> {}
    is SuccessNode -> {}
}
kotlin

Handling DaVinci flow collectors in continue nodes

The ContinueNode type contains collectors. These collectors define what information or action to request from the user, or client device.

There are specific collector types. For example there are TextCollector and PasswordCollector types.

To complete a DaVinci flow we recommend that you implement a component for each connector type you will encounter in the flow. Then you can iterate through the flow and handle each collector as you encounter it.

Access collectors in a ContinueNode
node.collectors.forEach {
    when(it) {
        is TextCollector → it.value = "My First Name"
        is PasswordCollector → it.value = "My Password"
        is SubmitCollector → it.value = "click me"
        is FlowCollector → it.value = "Forgot Password"
    }
}
kotlin

Continuing a DaVinci flow

After collecting the data for a node you can proceed to the next node in the flow by calling the next() method on your current node object.

Continue a DaVinci flow using next()
val next = node.next()
kotlin

You do not need to pass any parameters into the next method as the DaVinci client internally stores the updated object, ready to return to the PingOne server.

The server responds with a new node object, just like when starting a flow initially.

Loop again through conditional checks on the new node’s type to render the appropriate UI or take the appropriate action.

Handling DaVinci flow error nodes

DaVinci flows return the ErrorNode type when it receives data that is incorrect, but you can fix the data and resubmit. For example, an email value submitted in an invalid format or a new password that is too short.

You can retrieve the error message by using node.message(), and the raw JSON response with node.input.

Displaying the reason for an error
val node = daVinci.start() // Start the flow

//Determine the Node Type
when (node) {
    is ContinueNode -> {}
    is ErrorNode -> {
        node.message() // Retrieve the cause of the error
    }
    is FailureNode -> {}
    is SuccessNode -> {}
}
kotlin

This is different than a FailureNode type, which you cannot resubmit and must restart the entire flow.

You can retain a reference to the node you submit in case the next node you receive is an ErrorNode type. If so, you can re-render the previous form, and inject the error information from the new ErrorNode node.

After the user revises the data call next() as you did before.

Handling DaVinci flow failure nodes

DaVinci flows return the FailureNode type if there has been an issue that prevents the flow from continuing. For example, the flow times out or suffers a server error.

You can retrieve the cause of the failure by using node.cause(), which is a Throwable object.

Handling receipt of a FailureNode type
val node = daVinci.start() // Start the flow

//Determine the Node Type
when (node) {
    is ContinueNode -> {}
    is ErrorNode -> {}
    is FailureNode -> {
        node.cause() // Retrieve the error message
    }
    is SuccessNode -> {}
}
kotlin

You should offer to restart the flow on receipt of a FailureNode type.

Handling DaVinci flow success nodes

DaVinci flows return the SuccessNode type when the user completes the flow and PingOne issues them a session.

To retrieve the existing session, you can use the following code:

Handling receipt of a SuccessNode type
val user: User? = daVinci.user()

user?.let {
    it.accessToken()
    it.revoke()
    it.userinfo()
    it.logout()
}
kotlin

Leverage Jetpack Compose

The following shows how you could use the DaVinci client with Jetpack Compose:

ViewModel
// Define State that listen by the View
var state = MutableStateFlow<Node>(Empty)

//Start the DaVinci flow
val next = daVinci.start()

// Update the state
state.update {
    next
}

fun next(node: ContinueNode) {
    viewModelScope.launch {
        val next = node.next()
        state.update {
            next
        }
    }
}
kotlin
View
when (val node = state.node) {
    is ContinueNode -> {}
    is ErrorNode -> {}
    is FailureNode -> {}
    is SuccessNode -> {}
}
kotlin

Configure DaVinci client for iOS properties

Applies to:

  • DaVinci client for Android

  • DaVinci client for iOS

  • DaVinci client for JavaScript

Configure DaVinci client module for iOS properties to connect to PingOne and step through an associated DaVinci flow.

Installing and importing the DaVinci client

To use the DaVinci client for iOS, use Swift Package Manager (SPM) to add the dependencies to your project:

You can install this by using SPM (Swift Package Manager) on the generated iOS project.

  1. In Xcode, select your project and navigate to Package Dependencies.

  2. Click the sign, and add the Ping SDK for iOS repository, https://github.com/ForgeRock/forgerock-ios-sdk.git.

  3. Add the davinci library to the project.

Configuring connection properties

Create an instance of the DaVinci class by passing configuration to the createDaVinci method. This uses the underlying Oidc module to set configuration properties.

The following properties are available for configuring the DaVinci client for iOS:

Properties
Property Description Required?

discoveryEndpoint

Your PingOne server’s .well-known/openid-configuration endpoint.

Example:

https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration

Yes

clientId

The client_id of the OAuth 2.0 client profile to use.

For example, 6c7eb89a-66e9-ab12-cd34-eeaf795650b2

Yes

scopes

A set of scopes to request when performing an OAuth 2.0 authorization flow.

For example, "openid", "profile", "email", "address", "revoke".

Yes

redirectUri

The redirect_uri as configured in the OAuth 2.0 client profile.

This value must match a value configured in your OAuth 2.0 client.

For example, .

Yes

timeout

A timeout, in seconds, for each request that communicates with the server.

Default is 30 seconds.

No

acrValues

Request which flow the PingOne server uses by adding an Authentication Context Class Reference (ACR) parameter.

Enter a single DaVinci policy by using its flow policy ID.

Example:

"d1210a6b0b2665dbaa5b652221badba2"

No

Example

The following shows an example DaVinci client configuration, using the underlying Oidc module:

Configure DaVinci client connection properties
let daVinci = DaVinci.createDaVinci { config in
    // Oidc as module
    config.module(OidcModule.config) { oidcValue in
        oidcValue.clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"
        oidcValue.discoveryEndpoint = "https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"
        oidcValue.scopes = ["openid", "profile", "email", "address", "revoke"]
        oidcValue.redirectUri = ""
    }
}
swift

Stepping through DaVinci flows

To authenticate your users the DaVinci client for iOS must start the flow, and step through each node.

Starting a DaVinci flow

To start a DaVinci flow, call the start() method:

Start a DaVinci flow
var node = await daVinci.start()
swift

Determining DaVinci flow node type

Each step of the flow returns one of four node types:

ContinueNode

This type indicates there is input required from the client. The node object for this type contains a collector object, which describes the information it requires from the client.

SuccessNode

This type indicates the flow is complete, and authentication was successful.

ErrorNode

This type indicates an error in the data sent to the server. For example, an email address in an incorrect format, or a password that does not meet complexity requirements.

You can correct the error and resubmit to continue the flow.

FailureNode

This type indicates that the flow could not be completed and must be restarted. This can be caused by a server error, or a timeout.

You can use the helper functions to determine which node type the server has returned:

Determine node type.
switch (node) {
  case is ContinueNode: do {}
  case is ErrorNode: do {}
  case is FailureNode: do {}
  case is SuccessNode: do {}
}
swift

Handling DaVinci flow collectors in continue nodes

The ContinueNode type contains collectors. These collectors define what information or action to request from the user, or client device.

There are specific collector types. For example there are TextCollector and PasswordCollector types.

To complete a DaVinci flow we recommend that you implement a component for each connector type you will encounter in the flow. Then you can iterate through the flow and handle each collector as you encounter it.

Access collectors in a ContinueNode
node.collectors.forEach { item in
    switch(item) {
    case is TextCollector:
        (item as! TextCollector).value = "My First Name"
    case is PasswordCollector:
        (item as! PasswordCollector).value = "My Password"
    case is SubmitCollector:
        (item as! SubmitCollector).value = "click me"
    case is FlowCollector:
        (item as! FlowCollector).value = "Forgot Password"
    }
}
swift

Continuing a DaVinci flow

After collecting the data for a node you can proceed to the next node in the flow by calling the next() method on your current node object.

Continue a DaVinci flow using next()
let next = node.next()
swift

You do not need to pass any parameters into the next method as the DaVinci client internally stores the updated object, ready to return to the PingOne server.

The server responds with a new node object, just like when starting a flow initially.

Loop again through conditional checks on the new node’s type to render the appropriate UI or take the appropriate action.

Handling DaVinci flow error nodes

DaVinci flows return the ErrorNode type when it receives data that is incorrect, but you can fix the data and resubmit. For example, an email value submitted in an invalid format or a new password that is too short.

You can retrieve the error message by using node.message(), and the raw JSON response with node.input.

Displaying the reason for an error
let node = await daVinci.start() //Start the flow

//Determine the Node Type
switch (node) {
  case is ContinueNode: do {}
  case is FailureNode: do {}
  case is ErrorNode:
      (node as! ErrorNode).message //Retrieve the error message
  case is SuccessNode: do {}
}
swift

This is different than a FailureNode type, which you cannot resubmit and must restart the entire flow.

You can retain a reference to the node you submit in case the next node you receive is an ErrorNode type. If so, you can re-render the previous form, and inject the error information from the new ErrorNode node.

After the user revises the data call next() as you did before.

Handling DaVinci flow failure nodes

DaVinci flows return the FailureNode type if there has been an issue that prevents the flow from continuing. For example, the flow times out or suffers a server error.

You can retrieve the cause of the failure by using node.cause(), which is an Error instance.

Handling receipt of a FailureNode type
let node = await daVinci.start() //Start the flow

//Determine the Node Type
switch (node) {
  case is ContinueNode: do {}
  case is FailureNode:
    (node as! FailureNode).cause //Retrieve the cause of the Failure
  case is ErrorNode: do {}
  case is SuccessNode: do {}
}
swift

You should offer to restart the flow on receipt of a FailureNode type.

Handling DaVinci flow success nodes

DaVinci flows return the SuccessNode type when the user completes the flow and PingOne issues them a session.

To retrieve the existing session, you can use the following code:

Handling receipt of a SuccessNode type
let user: User? = await daVinci.user()

_ = await user?.token()
await user?.revoke()
_ = await user?.userinfo(cache: false)
await user?.logout()
swift

Leverage SwiftUI

The following shows how you could use the DaVinci client with SwiftUI:

ViewModel
//Define State that listen by the View

@Published var state: Node = EmptyNode()

//Start the DaVinci flow
let next = await daVinci.start()

//Update the state
state = next

func next(node: ContinueNode) {
   val next = await node.next()
   state = next

}
swift
View
if let node = state.node {
    switch node {
    case is ContinueNode:
        // Handle ContinueNode case
        break
    case is ErrorNode:
        // Handle Error case
        break
    case is FailureNode:
        // Handle Failure case
        break
    case is SuccessNode:
        // Handle Success case
        break
    default:
        break
    }
}
swift

Configure DaVinci client for JavaScript properties

Applies to:

  • DaVinci client for Android

  • DaVinci client for iOS

  • DaVinci client for JavaScript

Configure DaVinci client properties to connect to PingOne and step through an associated DaVinci flow.

Installing and importing the DaVinci client

To install and import the DaVinci client:

  1. Install the DaVinci client into your JavaScript apps using npm:

    Install the DaVinci client
    npm install @forgerock/davinci-client
    shell
  2. Import the DaVinci client as a named import:

    Import the DaVinci client
    import { davinci } from '@forgerock/davinci-client';
    javascript

Configuring connection properties

Pass a config object into davinci to initialize a client with the required connection properties.

The following properties are available for configuring the DaVinci client for JavaScript:

Properties
Property Description Required?

serverConfig

An interface for configuring how the SDK contacts the PingAM instance.

Contains wellknown and timeout.

Yes

serverConfig: {wellknown}

Your PingOne server’s .well-known/openid-configuration endpoint.

Example:

https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration

Yes

serverConfig: {timeout}

A timeout, in milliseconds, for each request that communicates with your server.

For example, for 30 seconds specify 30000.

Defaults to 5000 (5 seconds).

No

clientId

The client_id of the OAuth 2.0 client profile to use.

For example, 6c7eb89a-66e9-ab12-cd34-eeaf795650b2

Yes

scope

A list of scopes to request when performing an OAuth 2.0 authorization flow, separated by spaces.

For example, .

No

responseType

The type of OAuth 2.0 flow to use, either code or token.

Defaults to code.

No

Example

The following shows a full DaVinci client configuration:

Configure DaVinci client connection properties
import { davinci } from '@forgerock/davinci';

const davinciClient = await davinci({
  config: {
    clientId: '6c7eb89a-66e9-ab12-cd34-eeaf795650b2',
    serverConfig: {
      wellknown: 'https://auth.pingone.com/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration',
      timeout: 3000,
    },
    scope: '',
    responseType: 'code',
  },
});
javascript

Stepping through DaVinci flows

To authenticate your users the Ping SDK for JavaScript DaVinci client must start the flow, and step through each node.

Starting a DaVinci flow

To start a DaVinci flow, call the start() method on your new client object:

Start a DaVinci flow
let node = await davinciClient.start();
javascript

Determining DaVinci flow node type

Each step of the flow returns one of four node types:

continue

This type indicates there is input required from the client. The node object for this type contains a list of collector objects, which describe the information it requires from the client.

success

This type indicates the flow is complete, and authentication was successful.

error

This type indicates an error in the data sent to the server. For example, an email address in an incorrect format, or a password that does not meet complexity requirements.

You can correct the error and resubmit to continue the flow.

failure

This type indicates that the flow could not be completed and must be restarted. This can be caused by a server error, or a timeout.

Use node.status to determine which node type the server has returned:

Determine node type using the node.status property
let node = await davinciClient.start();

switch (node.status) {
  case 'continue':
    return renderContinue();
  case 'success':
    return renderSuccess();
  case 'error':
    return renderError();
  default: // Handle 'failure' node type
    return renderFailure();
}
javascript

Handling DaVinci flow collectors in continue nodes

The continue node type contains a list of collector objects. These collectors define what information or action to request from the user, or browser.

There are two categories of collector:

SingleValueCollector

A request for a single value, such as a username or password string.

Show the interface for SingleValueCollector
export interface SingleValueCollector {
  category: 'SingleValueCollector';
  type: SingleValueCollectorTypes;
  id: string;
  name: string;
  input: {
    key: string;
    value: string | number | boolean;
    type: string;
  };
  output: {
    key: string;
    label: string;
    type: string;
    value: string;
  };
}
javascript
ActionCollector

Represents an action the user can take, such as a button or a link they can select. For example, a link that jumps to a password recovery section of a flow.

Show the interface for ActionCollector
export interface ActionCollector {
  category: 'ActionCollector';
  type: ActionCollectorTypes;
  id: string;
  name: string;
  output: {
    key: string;
    label: string;
    type: string;
    url?: string;
  };
}
javascript

Within each category of collector there are specific collector types. For example, in the SingleValueCollector category there are TextCollector and PasswordCollector types.

To complete a DaVinci flow we recommend that you implement a component for each connector type you will encounter in the flow. Then you can iterate through the flow and handle each collector as you encounter it.

Example 1. TextCollector

This example shows how to update a collector with a value gathered from your user.

Pass both a collector and updater object into a component that renders the appropriate user interface, captures the user’s input, and then updates the collector, ready to return to the server.

Example TextCollector mapping
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'TextCollector') {
    renderTextCollector(collector, davinciClient.update(collector));
  }
});
javascript

Mutating the node object, the collectors array, or any other properties does not alter the internal state of the DaVinci client.

The internal data the client stores is immutable and can only be updated using the provided APIs, not through property assignment.

Your renderTextCollector would resemble the following:

Example TextCollector updater component
function renderTextCollector(collector, updater) {
  // ... component logic

  function onClick(event) {
    updater(event.target.value);
  }

  // render code
}
javascript
Example 2. FlowCollector

This example shows how change from the current flow to an alternate flow, such as a reset password or registration flow.

To switch flows, call the flow method on the davinciClient passing the key property to identify the new flow.

Example FlowCollector mapping
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'FlowCollector') {
    renderFlowCollector(collector, davinciClient.flow(collector));
  }
});
javascript

This returns a function you can call when the user interacts with it.

Example flowCollector component
function renderFlowCollector(collector, startFlow) {
  // ... component logic

  function onClick(event) {
    startFlow();
  }

  // render code
}
javascript
Example 3. SubmitCollector

This example shows how submit the current node and its collected values back to the server. The collection of the data is already complete so an updater component is not required. This collector only renders the button for the user to submit the collected data.

Example SubmitCollector mapping
const collectors = davinciClient.getCollectors();
collectors.map((collector) => {
  if (collector.type === 'SubmitCollector') {
    renderSubmitCollector(
      collector, // This is the only argument you will need to pass
    );
  }
});
javascript

Continuing a DaVinci flow

After collecting the data for a node you can proceed to the next node in the flow by calling the next() method on your DaVinci client object.

This can be the result of a user clicking on the button rendered from the SubmitCollector, from the submit event of an HTML form, or by programmatically triggering the submission in the application layer.

Continue a DaVinci flow using next()
let nextStep = davinciClient.next();
javascript

You do not need to pass any parameters into the next method as the DaVinci client internally stores the updated object, ready to return to the PingOne server.

The server responds with a new node object, just like when starting a flow initially.

Loop again through conditional checks on the new node’s type to render the appropriate UI or take the appropriate action.

Handling DaVinci flow error nodes

DaVinci flows return the error node type when it receives data that is incorrect, but you can fix the data and resubmit. For example, an email value submitted in an invalid format or a new password that is too short.

This is different than a failure node type which you cannot resubmit and instead you must restart the entire flow.

You can retain a reference to the node you submit in case the next node you receive is an error type. If so, you can re-render the previous form, and inject the error information from the new error node.

After the user revises the data call next() as you did before.

Handling DaVinci flow failure nodes

DaVinci flows return the failure node type if there has been an issue that prevents the flow from continuing. For example, the flow times out or suffers an HTTP 500 server error.

You should offer to restart the flow on receipt of a failure node type.

Restart a DaVinci flow on receipt of a failure node type
const node = await davinciClient.next();

if (node.status === 'failure') {
  const error = davinciClient.getError();
  renderError(error);

  // ... user clicks button to restart flow
  const freshNode = davinciClient.start();
}
javascript

Handling DaVinci flow success nodes

DaVinci flows return the success node type when the user completes the flow and PingOne issues them a session.

On receipt of a success node type you should use the OAuth 2.0 authorization Code and state properties from the node and use them to obtain an access token on behalf of the user.

To obtain an access token, leverage the Ping SDK for JavaScript.

Example of obtaining an access token using the Ping SDK for JavaScript
// ... other imports
import { davinci } from '@forgerock/davinci-client';
import { Config, TokenManager } from '@forgerock/javascript-sdk';

// ... other config or initialization code

// This Config.set accepts the same config schema as the davinci function
Config.set(config);

const node = await davinciClient.next();

if (node.status === 'success') {
  const clientInfo = davinciClient.getClient();

  const code = clientInfo.authorization?.code || '';
  const state = clientInfo.authorization?.state || '';

  const tokens = await TokenManager.getTokens({
    query: {
      code, state
    }
  });

  // user now has session and OIDC tokens
}
javascript

Default DaVinci client headers

Applies to:

  • DaVinci client for Android

  • DaVinci client for iOS

  • DaVinci client for JavaScript

The DaVinci clients send a number of header values to the server with every outgoing request.

These headers can help you identify the client in your flows and help you correlate actions to a transaction in DaVinci audit logs. You can also use these values to alter the course of a DaVinci flow.

The default headers the DaVinci client always include are as follows:

x-requested-with

Identifies that the request comes from an app built with the Ping DaVinci client.

Default value: ping-sdk

x-requested-platform

Identifies the platform the DaVinci client is running on.

Default values:

Platform Value

Android

android

iOS

ios

JavaScript

javascript

interactionId

Returns the interactionId value provided by the server to help trace the transaction in server audit logs and dashboards.

Example value: 18484499-c551-4d99-c415-b01c79bedb47

interactionToken

Returns the interactionToken value provided by the server to help trace the transaction in server audit logs and dashboards.

Example value: 437783552aa3a5a8f0041028d5b8dac2d72f7e7ebd7f88a966fb690402f6571b964c3df8897cbe542e62721070b3f6fcc946f4dd2bc80b9df332d39657fcaaad4651884093a786910d6f1337bd8dda17b4fca48e8fa481469ce0df1f676e46d1a6fc30577d910010d4a2530f2d02e69f436d610992c79fcb0ca87131d0df3f9a

Ping SDK for PingOne DaVinci tutorials

These tutorials walk you through updating a provided sample app so that it connects to a PingOne tenant to authenticate a user using the PingOne sign-on with sessions DaVinci flow.

This flow allows users to register, authenticate, and verify their email address with PingOne. It combines the PingOne Sign On and Password Reset flow, which allows users to reset or recover their passwords, with the PingOne Registration and Email Verification flow, which allows users to register and verify their email. In addition, this flow checks for an active user session before prompting for authentication.

Features of the PingOne sign-on with sessions flow
Figure 3. Overview of the PingOne sign-on with sessions flow

You will implement code that:

  • Configures the app with the connection settings for your PingOne instance.

  • Starts a DaVinci flow

  • Renders UI depending on the type of node encountered in the flow

  • Returns responses to each node encountered

  • Gets an access token for the user on completion of the flow

  • Gets the user’s details from PingOne, such as their full name and email address

  • Logs the user out of PingOne

To start, choose the platform to host your app:

DaVinci flow tutorial for Android

This tutorial walks you through updating a provided sample app so that it connects to a PingOne tenant to authenticate a user using the PingOne sign-on with sessions DaVinci flow.

This flow allows users to register, authenticate, and verify their email address with PingOne.

Before you begin

Before you begin this tutorial ensure you have set up your PingOne server with the required configuration.

For example, you will need to have an OAuth 2.0 client application set up.

Step 1. Download the samples

To start this tutorial, you need to download the SDK sample apps repo, which contains the projects you will use.

Step 2. Configure the sample app

In this step, you configure the sample app to connect to the OAuth 2.0 application you created in DaVinci.

Step 3. Test the app

To test the app, run the sample that you configured in the previous step.

The sample connects to your PingOne server to obtain the correct URIs to authenticate the user, and redirects the browser to your PingOne server.

After authentication, PingOne redirects the browser back to your application, which then obtains an OAuth 2.0 access token and displays the related user information.

Before you begin

To successfully complete this tutorial refer to the prerequisites in this section.

The tutorial also requires a configured PingOne instance.

Compatibility

PingOne
DaVinci flows

Ensure your user interface nodes only employ these supported connectors and capabilities:

  • HTTP Connector

    • Custom HTML Template

Compatible fields within the Custom HTML Template capability are:

  • Text field: Lets the user enter text

  • Password field: Lets the user enter a password which cannot be read from the screen

  • Submit Button: Lets the user submit field data and proceed

  • Flow Button: Lets the user trigger a new process without submitting field data

Verify that your flow does not depend on any unsupported elements:

  • SKPolling components: SKPolling components cannot be processed by the SDK and should not be included.

  • Variable field names: You cannot use variables or other values from a node’s input schema in the names of any field used by the SDK.

  • Images: Images included in the flow cannot be passed to the SDK.

  • Additional text: Titles or other text cannot be passed to the SDK.

Android

This sample requires at least Android API 23 (Android 6.0)

Java

This sample requires at least Java 8 (v1.8).

Prerequisites

Android Studio

Download and install Android Studio, which is available for many popular operating systems.

An Android emulator or physical device

To try the quick start application as you develop it, you need an Android device. To add a virtual, emulated Android device to Android Studio, refer to Create and manage virtual devices, on the Android Developers website.

Server configuration

You must configure your PingOne instance for use with the DaVinci client.

Ask your PingOne administrator to complete the following tasks:

  • Configure a DaVinci flow

  • Create a DaVinci application

  • Configure PingOne for DaVinci flow invocation

To learn how to complete these steps, refer to Launching a flow with a Ping SDK in the PingOne DaVinci documentation.

Step 1. Download the samples

To start this tutorial, you need to download the ForgeRock SDK sample apps repo, which contains the projects you will use.

  1. In a web browser, navigate to the SDK Sample Apps repository.

  2. Download the source code using one of the following methods:

    Download a ZIP file
    1. Click Code, and then click Download ZIP.

    2. Extract the contents of the downloaded ZIP file to a suitable location.

    Use a Git-compatible tool to clone the repo locally
    1. Click Code, and then copy the HTTPS URL.

    2. Use the URL to clone the repository to a suitable location.

      For example, from the command-line you could run:

The result of these steps is a local folder named sdk-sample-apps.

Step 2. Configure the sample app

In this section you open the sample project in Android Studio, and view the integration points in the TODO pane.

You’ll visit each integration point in the sample app to understand how to complete a DaVinci flow, including handling the different nodes and their collectors, obtaining an access token and user information, and finally signing out of the session.

  1. In Android Studio, click Open, navigate to the sdk-sample-apps/android/kotlin-davinci folder that has the downloaded sample source code in, and then click Open.

    Android Studio opens and loads the DaVinci tutorial project.

  2. In the Project pane, navigate to samples > app.

  3. On the View menu, select Tool Windows, and then click TODO.

    The sample code is annotated with TODO comments to help you locate the integration points, where code changes are required.

    android sample todo pane en
    Figure 4. Integration points annotated with TODO comments
  4. In the TODO pane, double-click the STEP 1 line.

    Android Studio opens DaVinciViewModel.kt:

    DaVinciViewModel.kt
    //TODO: Integration Point. STEP 1
    val daVinci = DaVinci {
        logger = Logger.STANDARD
    
        // Oidc as module
        module(Oidc) {
            clientId = "<Client ID>"
            discoveryEndpoint = "<Discovery Endpoint>"
            scopes = mutableSetOf("<scope1>", "<scope2>", "…​")
            redirectUri = "<Redirect URI>"
        }
    }
    kotlin

    This snippet initializes the DaVinci module, and leverages the OpenID Connect (OIDC) module to configure the settings to connect to your PingOne instance.

    1. Replace <Client ID> with the ID of the client you are connecting to in PingOne.

      Example:

      clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    2. Replace <Discovery Endpoint> with the OIDC Discovery Endpoint value from the client you are connecting to in PingOne.

      Example:

      discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    3. In the scopes property, add the scopes you want to assign users who complete authentication using the client.

      Example:

      scopes = mutableSetOf("openid", "email", "profile")

    4. Replace <Redirect URI> with the application ID of your sample app, followed by ://oauth2redirect.

      Example:

      redirectUri = ""

      The redirectUri value you use must exactly match one of the Redirect URIs value you enter in the native OAuth 2.0 application you created earlier.

    5. Optionally, delete the TODO comment to remove it from the list.

    The result resembles the following:

    DaVinciViewModel.kt
    val daVinci = DaVinci {
        logger = Logger.STANDARD
    
        // Oidc as module
        module(Oidc) {
            clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"
            discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"
            scopes = mutableSetOf("openid", "email", "profile")
            redirectUri = ""
        }
    }
    kotlin
  5. In the TODO pane, double-click the STEP 2 line.

    Android Studio opens DaVinciViewModel.kt:

    DaVinciViewModel.kt
    //TODO: Integration Point. STEP 2
    // Start the DaVinci flow, next node from the flow will be returned
    // Update the state with the next node
    
    /*
    val next = daVinci.start()
    
    state.update {
        it.copy(prev = next, node = next)
    }
    */
    kotlin

    This snippet calls start() to start the DaVinci flow, and assigns the returned node to the variable next.

    It also updates the app’s state to store the response as both prev and node.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  6. In the TODO pane, double-click the STEP 3 line.

    Android Studio opens DaVinciViewModel.kt:

    DaVinciViewModel.kt
    //TODO: Integration Point. STEP 3
    // Continue the DaVinci flow, next node from the flow will be returned
    // Update the state with the next node
    
    /*
    val next = current.next()
    
    state.update {
        it.copy(prev = current, node = next)
    }
    */
    kotlin

    This snippet calls next() to continue the DaVinci flow, by proceeding to the next available node. It assigns the newly returned node to the variable next.

    It also updates the app’s state to store the new response as node, and the current node as prev.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  7. In the TODO pane, double-click the STEP 4 line.

    Android Studio opens DaVinci.kt:

    DaVinci.kt
    //TODO: Integration Point. STEP 4
    // Render the current node depending on its type
    
    /*
    when (val node = state.node) {
        is ContinueNode → {
            Render(node = node, onNodeUpdated, onStart) { onNext(node) }
        }
        is FailureNode → {
            Log.e("DaVinci", node.cause.message, node.cause)
            Render(node = node)
        }
        is ErrorNode → {
            Render(node)
            // Render the previous node
            if (state.prev is ContinueNode) {
                Render(node = state.prev, onNodeUpdated, onStart) { onNext(state.prev) }
            }
        }
        is SuccessNode → {
            LaunchedEffect(true) { onSuccess?.let { onSuccess() } }
        }
        else → {}
    }
    */
    kotlin

    This snippet watches for a change in state, and takes an action based on the ode type returned by DaVinci.

    For example, if it is a FailureNode log an error message. If the node is a ContinueNode that continues the flow, call the render function to display the necessary fields on the screen.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  8. In the TODO pane, double-click the STEP 5 line.

    Android Studio opens ContinueNode.kt:

    ContinueNode.kt
    //TODO: Integration Point. STEP 5
    // Intermediate step in the Davinci Flow. The ContinueNode is a node that is used to
    // continue the flow. It can have multiple collectors that are used to collect user input.
    // Render the UI for each collector that are part of the ContinueNode.
    
    /*
    continueNode.collectors.forEach {
        when (it) {
            is FlowCollector → {
                hasAction = true
                FlowButton(it, onNext)
            }
    
            is PasswordCollector → {
                Password(it, onNodeUpdated)
            }
            is SubmitCollector → {
                hasAction = true
                SubmitButton(it, onNext)
            }
    
            is TextCollector → Text(it, onNodeUpdated)
        }
    }
    */
    kotlin

    This snippet handles the various collectors that are returned by the current node.

    Loop through all of the collectors in the node and render an appropriate field in your app.

    For example, this snippet handles text and password fields, and two types of button, a submit and a flow type.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  9. In the TODO pane, double-click the STEP 6 line.

    Android Studio opens TokenViewModel.kt:

    TokenViewModel.kt
    //TODO: Integration Point. STEP 6
    // Retrieve the access token
    
    /*
    User.user()?.let {
       when (val result = it.token()) {
            is Failure → {
                state.update {
                    it.copy(token = null, error = result.value)
                }
            }
    
            is Success → {
                state.update {
                    it.copy(token = result.value, error = null)
                }
            }
        }
    } ?: run {
        state.update {
            it.copy(token = null, error = null)
        }
    }
    */
    kotlin

    This snippet gets called when the flow reaches a SuccessNode and gets an access token on behalf of the authenticated user.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  10. In the TODO pane, double-click the STEP 7 line.

    Android Studio opens UserProfileViewModel.kt:

    UserProfileViewModel.kt
    //TODO: Integration Point. STEP 7
    // Retrieve the user info
    
    /*
    User.user()?.let { user →
        when (val result = user.userinfo(false)) {
            is Result.Failure →
                state.update { s →
                    s.copy(user = null, error = result.value)
                }
    
            is Result.Success →
                state.update { s →
                    s.copy(user = result.value, error = null)
                }
        }
    }
    */
    kotlin

    This snippet gets the user’s info from DaVinci, for example their preferred name for display within the sample app.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

  11. In the TODO pane, double-click the STEP 8 line.

    Android Studio opens LogoutViewModel.kt:

    LogoutViewModel.kt
    //TODO: Integration Point. STEP 8
    // logout the user
    
    /*
    User.user()?.logout()
    */
    kotlin

    This snippet calls the logout() function on the User object to sign the user out of the app, and end their session in DaVinci.

    1. Uncomment the highlighted text.

    2. Optionally, delete the TODO comment to remove it from the list.

Step 3. Test the app

In the following procedure, you run the sample app that you configured in the previous step.

  1. Add or connect a device to Android Studio.

    Learn more about devices in Android Studio in the Android Developer documentation:

  2. On the Run menu, click Run 'samples.app'.

    Android Studio starts the sample application on the simulated or connected device.

    The app automatically starts the DaVinci flow:

    The DaVinci sample app first screen with fields and buttons.
    Figure 5. The DaVinci sample app first screen with fields and buttons.
  3. Optionally, to register a new identity in PingOne, tap the No Account? Register now! link.

    This link is an example of a FlowButton.

    The app displays the registration screen:

    The DaVinci sample app registration screen
    Figure 6. The DaVinci sample app registration screen.
    1. Enter the details of the new identity, and then click Save.

      The app creates the new identity in PingOne and returns to the sign on screen.

  4. Enter the username and password of a PingOne identity, and then click Sign On.

    The app sends the credentials to PingOne for validation, and on success displays the user’s info:

    The DaVinci sample app displaying user info
    Figure 7. The DaVinci sample app displaying user info
  5. Tap the menu () icon, and then tap generating_tokens Show Token.

    The app shows the access token obtained on behalf of the user.

    The DaVinci sample app displaying a user’s access token
    Figure 8. The DaVinci sample app displaying a user’s access token
  6. Tap the menu () icon, and then tap logout Logout.

    The app revokes the existing tokens and ends the session in PingOne.

Troubleshooting

This section contains help if you encounter an issue running the sample code.

What can cause validation errors in the request?

When starting the app you might see an error message as follows:

The request could not be completed. One or more validation errors were in the request.

davinci sample validation error en
Figure 9. Validation error when starting the app

The Logcat pane in Android Studio often contains additional information about the error:

{
  "id" : "2a72bf00-5f20-4b78-a7d0-ad8d95e9b11b",
  "code" : "INVALID_DATA",
  "message" : "The request could not be completed. One or more validation errors were in the request.",
  "details" : [
    {
      "code" : "INVALID_VALUE",
      "target" : "redirect_uri",
      "message" : "Redirect URI mismatch"
    }
  ]
}
json

In this case the cause is Redirect URI mismatch.

Ensure that your redirectUri value in com/pingidentity/samples/app/davinci/DaVinciViewModel.kt exactly matches one of the values you entered in the Redirect URIs field in your OAuth 2.0 application in PingOne:

.Match the redirect URIs in the sample app and PingOne configuration
Figure 10. Match the redirect URIs in the sample app and PingOne configuration

DaVinci flow tutorial for iOS

This tutorial walks you through updating a provided sample app so that it connects to a PingOne tenant to authenticate a user using the PingOne sign-on with sessions DaVinci flow.

This flow allows users to register, authenticate, and verify their email address with PingOne.

Before you begin

Before you begin this tutorial ensure you have set up your PingOne server with the required configuration.

For example, you will need to have an OAuth 2.0 client application set up.

Step 1. Download the samples

To start this tutorial, you need to download the SDK sample apps repo, which contains the projects you will use.

Step 2. Configure the sample app

In this step, you configure the sample app to connect to the OAuth 2.0 application you created in DaVinci.

Step 3. Test the app

To test the app, run the sample that you configured in the previous step.

The sample connects to your PingOne server to obtain the correct URIs to authenticate the user, and redirects the browser to your PingOne server.

After authentication, PingOne redirects the browser back to your application, which then obtains an OAuth 2.0 access token and displays the related user information.

Before you begin

To successfully complete this tutorial refer to the prerequisites in this section.

The tutorial also requires a configured PingOne instance.

Compatibility

PingOne
DaVinci flows

Ensure your user interface nodes only employ these supported connectors and capabilities:

  • HTTP Connector

    • Custom HTML Template

Compatible fields within the Custom HTML Template capability are:

  • Text field: Lets the user enter text

  • Password field: Lets the user enter a password which cannot be read from the screen

  • Submit Button: Lets the user submit field data and proceed

  • Flow Button: Lets the user trigger a new process without submitting field data

Verify that your flow does not depend on any unsupported elements:

  • SKPolling components: SKPolling components cannot be processed by the SDK and should not be included.

  • Variable field names: You cannot use variables or other values from a node’s input schema in the names of any field used by the SDK.

  • Images: Images included in the flow cannot be passed to the SDK.

  • Additional text: Titles or other text cannot be passed to the SDK.

iOS

This sample app is compatible with iOS 12 and later.

Prerequisites

Xcode

You can download the latest version for free from https://developer.apple.com/xcode/.

Server configuration

You must configure your PingOne instance for use with the DaVinci client.

Ask your PingOne administrator to complete the following tasks:

  • Configure a DaVinci flow

  • Create a DaVinci application

  • Configure PingOne for DaVinci flow invocation

To learn how to complete these steps, refer to Launching a flow with a Ping SDK in the PingOne DaVinci documentation.

Step 1. Download the samples

To start this tutorial, you need to download the ForgeRock SDK sample apps repo, which contains the projects you will use.

  1. In a web browser, navigate to the SDK Sample Apps repository.

  2. Download the source code using one of the following methods:

    Download a ZIP file
    1. Click Code, and then click Download ZIP.

    2. Extract the contents of the downloaded ZIP file to a suitable location.

    Use a Git-compatible tool to clone the repo locally
    1. Click Code, and then copy the HTTPS URL.

    2. Use the URL to clone the repository to a suitable location.

      For example, from the command-line you could run:

The result of these steps is a local folder named sdk-sample-apps.

Step 2. Configure the sample app

In this section you open the sample project in Xcode, and view the integration points in the TODO pane.

You’ll visit each integration point in the sample app to understand how to complete a DaVinci flow, including handling the different nodes and their collectors, obtaining an access token and user information, and finally signing out of the session.

  1. In Xcode, on the File menu, click Open.

  2. Navigate to the sdk-sample-apps folder you cloned in the previous step, navigate to iOS > swiftui-davinci > Davinci.xcworkspace, and then click Open.

    Xcode opens and loads the DaVinci tutorial project.

  3. Open DavinciViewModel and locate the DaVinci.createDaVinci call:

    The DaVinci.createDaVinci call in DavinciViewModel
    public let davinci = DaVinci.createDaVinci { config in
      //TODO: Provide here the Server configuration. Add the PingOne server Discovery Endpoint and the OAuth 2.0 client details
      config.module(OidcModule.config) { oidcValue in
        oidcValue.clientId = "Client ID"
        oidcValue.scopes = ["scope1", "scope2", "scope3"]
        oidcValue.redirectUri = "Redirect URI"
        oidcValue.discoveryEndpoint = "Discovery Endpoint"
      }
    }
    kotlin

    This snippet initializes the DaVinci module, and leverages the OpenID Connect (OIDC) module to configure the settings to connect to your PingOne instance.

    1. In the oidcValue.clientId property, enter the ID of the client you are connecting to in PingOne.

      Example:

      clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    2. In the oidcValue.scopes property, add the scopes you want to assign users who complete authentication using the client.

      Example:

      scopes = mutableSetOf("openid", "email", "profile")

    3. In the oidcValue.redirectUri property, enter the application ID of your sample app, followed by ://oauth2redirect.

      Example:

      redirectUri = ""

      The redirectUri value you use must exactly match one of the Redirect URIs value you enter in the native OAuth 2.0 application you created earlier.

    4. In the oidcValue.discoveryEndpoint property, enter the OIDC Discovery Endpoint value from the client you are connecting to in PingOne.

      Example:

      discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    5. Optionally, delete the TODO comment to remove it from the list.

    The result resembles the following:

    DavinciViewModel
    public let davinci = DaVinci.createDaVinci { config in
      //TODO: Provide here the Server configuration. Add the PingOne server Discovery Endpoint and the OAuth2.0 client details
      config.module(OidcModule.config) { oidcValue in
        oidcValue.clientId = "6c7eb89a-66e9-ab12-cd34-eeaf795650b2"
        oidcValue.scopes = ["openid", "email", "profile"]
        oidcValue.redirectUri = ""
        oidcValue.discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"
      }
    }
    kotlin

Step 3. Test the app

In the following procedure, you run the sample app that you configured in the previous step. The app performs a centralized login on your PingOne instance.

Log in as a demo user

  1. In Xcode, select Product  Run.

    Xcode launches the sample app in the iPhone simulator.

    ios kotlin davinci main menu
    Figure 11. The iOS DaVinci sample main menu
  2. In the sample app on the iPhone simulator, tap Launch Davinci.

    The sample app launches the DaVinci flow configured in the OAuth 2.0 profile.

    ios kotlin davinci login
    Figure 12. The DaVinci sample app first screen with fields and buttons.
  3. Optionally, to register a new identity in PingOne:

    1. Tap the No Account? Register now! link.

      This link is an example of a FlowButton.

      The app displays the registration screen:

      The DaVinci sample app registration screen
      Figure 13. The DaVinci sample app registration screen.
    2. Enter the details of the new identity, and then click Save.

      The app creates the new identity in PingOne and returns to the sign on screen.

  4. Sign on as a demo user:

    • Name:

    • Password: Ch4ng3it!

    If authentication is successful, the application displays the tokens issued by PingOne.

    ios kotlin davinci access token
    Figure 14. The token issues by the DaVinci flow
  5. Tap Davinci to go back to the main menu, and then tap User Info.

    The app retrieves the user’s info from the /userinfo endpoint and displays it on screen:

    ios kotlin davinci userinfo
    Figure 15. User info retrieved from PingOne
  6. Tap Davinci to go back to the main menu, and then tap Logout.

    The sample app displays a logout button.

    ios kotlin davinci logout
    Figure 16. Logout button in the iOS sample app
  7. Tap Proceed to logout

    The app revokes the existing tokens and ends the session in PingOne.

    To verify the user is signed out:

    1. In the PingOne administration console, navigate to Directory > Users.

    2. Select the user you signed in as.

    3. From the Sevices dropdown, select Authentication:

      pingone sessions en
      Figure 17. Checking a user’s sessions in PingOne.

      The Sessions section displays any existing sessions the user has, and whether they originate from a mobile device.

DaVinci flow tutorial for JavaScript

This tutorial walks you through updating a provided sample app so that it connects to a PingOne tenant to authenticate a user using the PingOne sign-on with sessions DaVinci flow.

This flow allows users to register, authenticate, and verify their email address with PingOne.

Before you begin

Before you begin this tutorial ensure you have set up your PingOne instance with the required configuration.

For example, you will need an OAuth 2.0 client application set up.

Step 1. Download the samples

To start this tutorial, you need to download the SDK sample apps repo, which contains the projects you will use.

Step 2. Install the dependencies

The sample projects need a number of dependencies that you can install by using the npm command.

For example, the Ping SDK for JavaScript itself is one of the dependencies.

Step 3. Configure connection properties

In this step, you configure the "embedded-login" sample app to connect to the OAuth 2.0 application you created in PingOne Advanced Identity Cloud or PingAM.

Step 4. Test the app

The final step is to run the sample app. The sample connects to your server and walks through your authentication journey or tree.

After successful authentication, the sample obtains an OAuth 2.0 access token and displays the related user information.

Before you begin

To successfully complete this tutorial refer to the prerequisites in this section.

The tutorial also requires a configured server.

Compatibility

PingOne
DaVinci flows

Ensure your user interface nodes only employ these supported connectors and capabilities:

  • HTTP Connector

    • Custom HTML Template

Compatible fields within the Custom HTML Template capability are:

  • Text field: Lets the user enter text

  • Password field: Lets the user enter a password which cannot be read from the screen

  • Submit Button: Lets the user submit field data and proceed

  • Flow Button: Lets the user trigger a new process without submitting field data

Verify that your flow does not depend on any unsupported elements:

  • SKPolling components: SKPolling components cannot be processed by the SDK and should not be included.

  • Variable field names: You cannot use variables or other values from a node’s input schema in the names of any field used by the SDK.

  • Images: Images included in the flow cannot be passed to the SDK.

  • Additional text: Titles or other text cannot be passed to the SDK.

Prerequisites

Node and NPM

The SDK requires a minimum Node.js version of 18, and is tested on versions 18 and 20. To get a supported version of Node.js, refer to the Node.js download page.

You will also need npm to build the code and run the samples.

Server configuration

You must configure your PingOne instance for use with the DaVinci client.

Ask your PingOne administrator to complete the following tasks:

  • Configure a DaVinci flow

  • Create a DaVinci application

  • Configure PingOne for DaVinci flow invocation

To learn how to complete these steps, refer to Launching a flow with a Ping SDK in the PingOne DaVinci documentation.

Step 1. Download the samples

To start this tutorial, you need to download the SDK sample apps repo, which contains the projects you will use.

  1. In a web browser, navigate to the SDK Sample Apps repository.

  2. Download the source code using one of the following methods:

    Download a ZIP file
    1. Click Code, and then click Download ZIP.

    2. Extract the contents of the downloaded ZIP file to a suitable location.

    Use a Git-compatible tool to clone the repo locally
    1. Click Code, and then copy the HTTPS URL.

    2. Use the URL to clone the repository to a suitable location.

      For example, from the command-line you could run:

      git clone https://github.com/ForgeRock/sdk-sample-apps.git
      shell

The result of these steps is a local folder named sdk-sample-apps.

Step 2. Install the dependencies

In the following procedure, you install the required modules and dependencies, including the Ping SDK for JavaScript.

  1. In a terminal window, navigate to the sdk-sample-apps/javascript/embedded-login-davinci folder.

  2. To install the required packages, enter the following:

    npm install
    shell

    The npm tool downloads the required packages, and places them inside a node_modules folder.

Step 3. Configure connection properties

In this step, you configure the sample app to connect to the authentication tree/journey you created when setting up your server configuration.

  1. Copy the .env.example file in the sdk-sample-apps/javascript/embedded-login-davinci folder and save it with the name .env within this same directory.

  2. Open the .env file and edit the property values to match the settings you configured in previous steps:

    VITE_SCOPE=$SCOPE
    VITE_WEB_OAUTH_CLIENT=$WEB_OAUTH_CLIENT
    VITE_WELLKNOWN_URL=$WELLKNOWN_URL
    properties
    1. In the VITE_SCOPE property, enter the scopes you want to assign users who complete authentication using the client, separated by spaces.

      Example:

      VITE_SCOPE=""

    2. In VITE_WEB_OAUTH_CLIENT, property, enter the ID of the client you are connecting to in PingOne.

      Example:

      VITE_WEB_OAUTH_CLIENT="6c7eb89a-66e9-ab12-cd34-eeaf795650b2"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    3. In the VITE_WELLKNOWN_URL property, enter the OIDC Discovery Endpoint value from the client you are connecting to in PingOne.

      Example:

      discoveryEndpoint = "https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"

      Refer to Get configuration values from PingOne for instructions of where to find this value.

    The result resembles the following:

    VITE_SCOPE=""
    VITE_WEB_OAUTH_CLIENT=""
    VITE_WELLKNOWN_URL="https://auth.pingone.ca/3072206d-c6ce-ch15-m0nd-f87e972c7cc3/as/.well-known/openid-configuration"
    properties

Step 4. Test the app

In the following procedure, you run the sample app that you configured in the previous step. The sample connects to your server and walks through the authentication journey you created in an earlier step.

After successful authentication, the sample obtains an OAuth 2.0 access token and displays the related user information.

  1. In a terminal window, navigate to the sdk-sample-apps/javascript/embedded-login-davinci folder.

  2. Use npm to run the sample:

    npm run dev
    shell
  3. In a web browser:

    1. Ensure you are NOT currently logged into the PingOne instance.

      If you are logged into the PingOne instance in the browser, the sample will not work.

      Logout of the PingAM instance before you run the sample.

    2. Navigate to the following URL:

      A form appears with "Username" and "Password" fields:

      The login page of the JavaScript DaVinci client sample.
      Figure 18. The login page of the JavaScript DaVinci client sample.
  4. Optionally, to register a new identity in PingOne, tap the No Account? Register now! link.

    This link is an example of a FlowButton.

    The app displays the registration screen:

    The DaVinci sample app registration screen
    Figure 19. The DaVinci sample app registration screen.
    1. Enter the details of the new identity, and then click Save.

      The app creates the new identity in PingOne and returns to the sign on screen.

  5. Enter the username and password of a PingOne identity, and then click Sign On.

    The app sends the credentials to PingOne for validation, and on success displays the user’s session info:

    The DaVinci sample app displaying session info
    Figure 20. The DaVinci sample app displaying session info
  6. To get an access token for the user, click Get Tokens.

    The DaVinci sample app displaying tokens
    Figure 21. The DaVinci sample app displaying tokens
  7. To revoke the OAuth 2.0 token, click the Logout button.

    The application calls the /as/revoke endpoint to revoke the OAuth 2.0 token, and returns to the sign-in form.