Configure DaVinci client for JavaScript properties
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:
-
Install the DaVinci client into your JavaScript apps using
npm
:Install the DaVinci clientnpm install @forgerock/davinci-client
-
Import the DaVinci client as a named import:
Import the DaVinci clientimport { davinci } from '@forgerock/davinci-client';
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:
Property | Description | Required? |
---|---|---|
|
An interface for configuring how the SDK contacts the PingAM instance. Contains |
Yes |
|
Your PingOne server’s Example:
|
Yes |
|
A timeout, in milliseconds, for each request that communicates with your server. For example, for 30 seconds specify Defaults to |
No |
|
The For example, |
Yes |
|
A list of scopes to request when performing an OAuth 2.0 authorization flow, separated by spaces. For example, |
No |
|
The type of OAuth 2.0 flow to use, either Defaults to |
No |
Example
The following shows a full DaVinci client configuration:
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: 'openid profile email address revoke',
responseType: 'code',
},
});
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:
let node = await davinciClient.start();
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 ofcollector
objects, which describe the information it requires from the client.Learn more in Handling DaVinci flow collectors in continue nodes.
success
-
This type indicates the flow is complete, and authentication was successful.
Learn more in Handling DaVinci flow success nodes.
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.
Learn more in Handling DaVinci flow error nodes.
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.
Learn more in Handling DaVinci flow failure nodes.
Use node.status
to determine which node type the server has returned:
node.status
propertylet 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();
}
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; }; }
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; }; }
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.
TextCollector
mappingconst collectors = davinciClient.getCollectors();
collectors.map((collector) => {
if (collector.type === 'TextCollector') {
renderTextCollector(collector, davinciClient.update(collector));
}
});
Mutating the 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:
TextCollector
updater componentfunction renderTextCollector(collector, updater) {
// ... component logic
function onClick(event) {
updater(event.target.value);
}
// render code
}
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.
FlowCollector
mappingconst collectors = davinciClient.getCollectors();
collectors.map((collector) => {
if (collector.type === 'FlowCollector') {
renderFlowCollector(collector, davinciClient.flow(collector));
}
});
This returns a function you can call when the user interacts with it.
flowCollector
componentfunction renderFlowCollector(collector, startFlow) {
// ... component logic
function onClick(event) {
startFlow();
}
// render code
}
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.
SubmitCollector
mappingconst collectors = davinciClient.getCollectors();
collectors.map((collector) => {
if (collector.type === 'SubmitCollector') {
renderSubmitCollector(
collector, // This is the only argument you will need to pass
);
}
});
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.
next()
let nextStep = davinciClient.next();
You do not need to pass any parameters into the |
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.
failure
node typeconst node = await davinciClient.next();
if (node.status === 'failure') {
const error = davinciClient.getError();
renderError(error);
// ... user clicks button to restart flow
const freshNode = davinciClient.start();
}
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.
// ... 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
}