| name | ping-orchestration-javascript-sdk |
| description | Guide for building web apps with the Ping Orchestration JavaScript SDK (also referred to as the Ping Identity JavaScript SDK, formerly the ForgeRock JavaScript SDK / @forgerock/javascript-sdk; npm packages: `@forgerock/journey-client`, `@forgerock/davinci-client`, `@forgerock/oidc-client`). Use when the user wants to add Ping authentication to a web app — phrases like "add Ping auth to my web app", "use PingOne in React", "set up Journey in JavaScript", "build a Ping login page", "use DaVinci in my SPA", "integrate @forgerock/journey-client", "set up @forgerock/davinci-client", "migrate from @forgerock/javascript-sdk to the new SDK", or "add OIDC login to my React app". Detects framework (React active; Angular, Vue, vanilla JS on the roadmap), collects shared config, delegates Journey to ping-orchestration-reactjs-journey-sdk and DaVinci to ping-orchestration-reactjs-davinci-sdk, and handles OIDC centralized login inline. Covers both new sample app creation and integrating into an existing project. |
| license | MIT |
| metadata | {"author":"Ping Identity","version":"1.0.0"} |
Ping JavaScript SDK
First point of contact for building web apps with the Ping Orchestration JavaScript SDK (@forgerock/journey-client, @forgerock/davinci-client, @forgerock/oidc-client). Handles three authentication flows:
- Journey — callback-based auth against PingAM or PingOne AIC → delegates to
ping-orchestration-reactjs-journey-sdk
- DaVinci — collector-based auth against PingOne DaVinci → delegates to
ping-orchestration-reactjs-davinci-sdk
- OIDC centralized login — OAuth2 authorization code flow (browser redirect) → handled inline
Runs a three-step wizard (W1 → W2 → W3) to determine flow type, detect framework, collect shared config, then hands off or generates code.
Framework Registry
This table is the single source of truth for framework routing. The decision tree references it by Status. To add a new framework: change its status from placeholder to active, add delegate skill names, fill in the OIDC templates directory, and add any build-tool-specific guidance to the "Build Tool Configuration" subsection.
| Framework | Journey Delegate | DaVinci Delegate | OIDC Templates | Status |
|---|
| React | ping-orchestration-reactjs-journey-sdk | ping-orchestration-reactjs-davinci-sdk | assets/react/ | active |
| Angular | — | — | assets/angular/ | placeholder |
| Vue | — | — | assets/vue/ | placeholder |
| Vanilla JS | — | — | assets/vanilla/ | placeholder |
Wizard
Run these three steps every time the skill is invoked without a create-sample argument.
W1 — Flow type
Ask:
Which flow are you building?
- Journey — callback-based auth against PingAM or PingOne AIC
- DaVinci — collector-based auth against PingOne DaVinci
- OIDC centralized login — OAuth2/OIDC authorization code flow (browser redirect, no custom UI required)
W1b — Framework detection
Before collecting config, detect or ask about the user's framework.
If a package.json is present, run:
grep -E '"(react|vue|@angular/core|vite)"' package.json
react found → React detected → proceed to W2.
vue or @angular/core found → announce placeholder and offer choices:
"A dedicated ping-orchestration-vue-sdk / ping-orchestration-angular-sdk skill is on the way. I can:
- Generate framework-neutral SDK wiring — client init, token exchange, and route guard pseudocode you can adapt to your framework's patterns.
- Route to the React skill (
ping-orchestration-reactjs-journey-sdk / ping-orchestration-reactjs-davinci-sdk) as a reference implementation you can port.
Which would you prefer?"
- If the user chooses option 1 → proceed to W2 and then use the Generic (Placeholder Framework) Path below.
- If the user chooses option 2 → proceed to W2 and delegate to the React skill.
react-native found (check separately) → announce that ping-orchestration-react-native-sdk is on the way; no stopgap exists.
- Nothing found, or no
package.json → ask: "Which JavaScript framework are you using?" List active frameworks from the Framework Registry. If the user names a placeholder framework, offer the same two options above.
W2 — Shared configuration
Collect the following parameters. Skip any the user has already provided in their message.
| Parameter | Required for | Notes |
|---|
wellknown | All flows | Full OIDC discovery URL ending in .well-known/openid-configuration |
clientId | All flows | OAuth client ID registered in PingOne/AIC |
redirectUri | All flows | Must match the registered value exactly — include /callback path suffix |
scope | All flows | Space-separated; must include openid |
appName | All flows | Used for project/directory naming |
journeyName | Journey only | Authentication tree name (e.g., Login) |
acrValues | DaVinci/OIDC only | Optional ACR values string |
Validation before proceeding to W3:
wellknown must end with .well-known/openid-configuration
scope must include openid — if missing, add it and tell the user
redirectUri must be an absolute URL starting with http:// or https://
W3 — Confirm and handoff
Display a summary table:
| Parameter | Value |
|---|
| Flow | <journey | davinci | oidc-centralized> |
| Framework | <React> |
| Well-known URL | <wellknown> |
| Client ID | <clientId> |
| Redirect URI | <redirectUri> |
| Scope | <scope> |
| App name | <appName> |
| Journey name (Journey only) | <journeyName> |
| ACR values (DaVinci/OIDC, if provided) | <acrValues> |
Ask: "Does this look right? Shall I proceed?"
On confirmation:
- Journey (active framework) → invoke the Journey delegate from the Framework Registry via the Skill tool. Pass the collected config in your invocation context. Do not inline that skill's content. Note: the Journey delegate uses its own env var naming (
VITE_WELLKNOWN_URL, VITE_WEB_OAUTH_CLIENT, etc.) — the delegate will handle this; pass the logical values from W2.
- DaVinci (active framework) → invoke the DaVinci delegate from the Framework Registry via the Skill tool. Pass the collected config. Do not inline that skill's content. Note: the DaVinci delegate uses its own env var naming (
VITE_CLIENT_ID, VITE_DISCOVERY_ENDPOINT, etc.) — pass the logical values from W2.
- OIDC centralized (active framework) → read
assets/oidc-centralized-reference.md and continue to the OIDC Centralized Login section below. Use templates from the framework's OIDC Templates directory.
- Any flow (placeholder framework, generic path) → continue to the Generic (Placeholder Framework) Path section below.
Generic (Placeholder Framework) Path
Use this path when the user's framework is placeholder in the Framework Registry and they chose framework-neutral SDK wiring in W1b. The SDK packages are pure JavaScript with no framework dependency — this path provides the integration wiring without framework-specific component rendering.
What to generate
For each flow type, produce a single integration module the user can adapt to their framework:
Journey:
import { journey, callbackType } from '@forgerock/journey-client';
import { oidc } from '@forgerock/oidc-client';
const oidcClient = await oidc({ config: {
serverConfig: { wellknown: '<wellknown>' },
clientId: '<clientId>',
redirectUri: '<redirectUri>',
scope: '<scope>',
} });
const client = await journey({
serverConfig: { wellknown: '<wellknown>' },
journeyName: '<journeyName>',
});
let step = await client.start();
while (step.type === 'Step') {
for (const cb of step.callbacks) {
}
step = await client.next(step);
}
if (step.type === 'SuccessStep') {
} else {
}
DaVinci:
import { davinci } from '@forgerock/davinci-client';
const client = await davinci({
serverConfig: { wellknown: '<wellknown>' },
clientId: '<clientId>',
redirectUri: '<redirectUri>',
scope: '<scope>',
});
let node = await client.start();
while (node.status === 'continue') {
const collectors = node.client.collectors;
for (const collector of collectors) {
}
node = await client.next();
}
if (node.status === 'success') {
} else {
}
OIDC centralized:
import { oidc } from '@forgerock/oidc-client';
const CONFIG = {
serverConfig: { wellknown: '<wellknown>' },
clientId: '<clientId>',
redirectUri: '<redirectUri>',
scope: '<scope>',
};
const client = await oidc({ config: CONFIG });
const url = await client.authorize.url();
window.location.href = url;
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
if (code && state) {
await client.token.exchange(code, state);
window.location.replace('/');
}
const tokens = await client.token.get();
await client.user.logout();
Guidance to include
After generating the integration module:
- Explain which parts map to framework concerns (the render loop → component tree; the auth check → route guard; the callback page → dedicated route).
- Point to the React delegate skill as a full reference implementation they can study.
- Offer to help wire the module into their specific framework's patterns if they share their app structure.
Shared Setup Guidance
This section applies regardless of flow type or framework. Provide this guidance before delegating or generating code.
SDK setup (all frameworks)
Full package reference: references/sdk-packages.md
Install only the packages your flow needs:
npm install @forgerock/journey-client @forgerock/oidc-client
npm install @forgerock/davinci-client
npm install @forgerock/oidc-client
npm install @forgerock/protect
npm install @forgerock/device-client
Common pitfalls (SDK-level)
redirectUri mismatch: the value must match what's registered in PingOne/AIC exactly — trailing slash, port number, and path all matter.
- Missing
openid scope: @forgerock/oidc-client requires openid in scope or token exchange will fail.
- CORS: the well-known endpoint must include your app's origin in the server's CORS policy. Check PingOne/AIC CORS settings if you see preflight failures.
wellknown vs serverConfig: the new SDK (@forgerock/journey-client v2+) uses wellknown only. The legacy serverConfig.baseUrl is not supported.
Build tool configuration
Apply the section below that matches the detected framework. Only one applies per project.
Note for placeholder frameworks (Angular, Vue, Vanilla JS): Full skill support for these frameworks is not yet active. The sections below document the correct build tool conventions so you can configure your environment correctly when using the Generic Placeholder Framework Path. No OIDC templates are generated for placeholder frameworks — adapt the patterns manually.
React + Vite
Env vars use the VITE_ prefix (required by Vite for client-side exposure):
VITE_PING_WELLKNOWN=https://auth.example.com/am/oauth2/alpha/.well-known/openid-configuration
VITE_PING_CLIENT_ID=my-app
VITE_PING_REDIRECT_URI=http://localhost:5173/callback
VITE_PING_SCOPE=openid profile
# Journey only:
VITE_PING_JOURNEY_NAME=Login
# DaVinci/OIDC optional:
VITE_PING_ACR_VALUES=
Reference: assets/react/.env.template
Vite-specific pitfalls:
- Only vars prefixed
VITE_ are exposed to the browser bundle. Server-only secrets must stay unprefixed.
- Do not put a static
public/callback.html with an inline oidc().token.exchange() script. When @forgerock/oidc-client calls authorize.background() for silent token renewal, it opens a hidden iframe to the authorization endpoint which redirects back to the redirectUri. If a static script at that URL instantiates a second OIDC client and starts exchanging the code, it hijacks the iframe and causes an iframe timeout error. Instead, handle the callback inside the React app as a normal route (see oidc-callback.jsx.template).
Angular CLI
Env vars go in src/environments/environment.ts:
export const environment = {
pingWellknown: 'https://auth.example.com/am/oauth2/alpha/.well-known/openid-configuration',
pingClientId: 'my-app',
pingRedirectUri: 'http://localhost:4200/callback',
pingScope: 'openid profile',
pingJourneyName: 'Login',
};
Angular-specific notes:
- Use
environment.ts / environment.prod.ts for config — Angular does not expose process.env to the browser.
- The callback route is a normal Angular route component — no static HTML workaround needed since Angular's router handles it.
Vue CLI / Vite
Env vars use the prefix matching your build tool:
- Vite →
VITE_ prefix (same as React + Vite above)
- Vue CLI (webpack) →
VUE_APP_ prefix
VUE_APP_PING_WELLKNOWN=https://auth.example.com/am/oauth2/alpha/.well-known/openid-configuration
VUE_APP_PING_CLIENT_ID=my-app
VUE_APP_PING_REDIRECT_URI=http://localhost:8080/callback
VUE_APP_PING_SCOPE=openid profile
Access with process.env.VUE_APP_* (Vue CLI) or import.meta.env.VITE_* (Vite).
Vanilla JS / Other
No framework-specific env conventions. Use any approach that suits the project:
- A
config.js file exporting constants
.env loaded via dotenv at build time
- Inline
<script> variables for zero-build setups
create-sample command
/ping-orchestration-javascript-sdk create-sample "<description>"
Steps:
- Analyse the description to determine flow type (journey / davinci / oidc-centralized) and framework.
- If flow type is ambiguous, ask one clarifying question.
- If framework is not React and is not detectable from the project, ask.
- Collect any missing required parameters from W2 that are not inferable from the description.
- If framework is
active in the Framework Registry:
- Journey → invoke the Journey delegate via Skill tool with pre-filled config.
- DaVinci → invoke the DaVinci delegate via Skill tool with pre-filled config.
- OIDC centralized → read
assets/oidc-centralized-reference.md and generate templates from the framework's OIDC Templates directory with substituted placeholders.
- If framework is
placeholder → use the Generic (Placeholder Framework) Path.
Template placeholders (OIDC centralized only):
| Placeholder | Value |
|---|
PLACEHOLDER_APP_NAME | appName |
PLACEHOLDER_WELLKNOWN | wellknown |
PLACEHOLDER_CLIENT_ID | clientId |
PLACEHOLDER_REDIRECT_URI | redirectUri |
PLACEHOLDER_SCOPE | scope |
PLACEHOLDER_ACR_VALUES | acrValues — omit the line if not provided |
OIDC Centralized Login
Handled inline — no delegation. Uses @forgerock/oidc-client directly. Full API reference: assets/oidc-centralized-reference.md.
Flow
- User lands on login page → call
client.authorize.url() to generate the authorization URL → redirect the browser.
- User authenticates on PingOne/AIC → browser redirected back to
redirectUri.
- Callback page reads
code and state from the URL → calls client.token.exchange(code, state) → tokens stored.
- App calls
client.token.get() to read tokens → user is authenticated, render protected content.
- Background renewal: call
client.token.get({ backgroundRenew: true }) to keep the session alive via a hidden iframe.
- Logout: call
client.user.logout() → revokes the access token and ends the session on the server.
Client initialisation
import { oidc } from '@forgerock/oidc-client';
const CONFIG = {
serverConfig: {
wellknown: import.meta.env.VITE_PING_WELLKNOWN,
},
clientId: import.meta.env.VITE_PING_CLIENT_ID,
redirectUri: import.meta.env.VITE_PING_REDIRECT_URI,
scope: import.meta.env.VITE_PING_SCOPE,
};
const client = await oidc({ config: CONFIG });
Token exchange (callback page)
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
if (code && state) {
await client.token.exchange(code, state);
window.location.replace('/');
}
Route guard pattern
The route guard logic is the same for all frameworks — only the component wrapper changes.
Core logic (framework-agnostic):
async function checkAuth(client) {
const tokens = await client.token.get();
if (!tokens) {
const url = await client.authorize.url();
window.location.href = url;
return null;
}
return tokens;
}
React wrapper:
import { useEffect, useState } from 'react';
export function ProtectedRoute({ children }) {
const [isAuthenticated, setIsAuthenticated] = useState(null);
useEffect(() => {
client.token.get().then((tokens) => {
setIsAuthenticated(!!tokens);
});
}, []);
if (isAuthenticated === null) return <div>Loading…</div>;
if (!isAuthenticated) {
client.authorize.url().then((url) => { window.location.href = url; });
return null;
}
return children;
}
For other frameworks, adapt the wrapper to the framework's guard mechanism (Angular CanActivate, Vue beforeEach navigation guard, etc.) using the same checkAuth logic.
Templates
Generate these files when flow type is oidc-centralized. Read templates from the active framework's subdirectory in the Framework Registry's OIDC Templates column (e.g., assets/react/ for React). Substitute placeholders and write to the user's project directory.
React (assets/react/):
| Template file | Output path | Purpose |
|---|
assets/react/.env.template | .env | Environment variables (Vite VITE_ prefix) |
assets/react/oidc-app.jsx.template | src/App.jsx | App shell with OIDC context + protected route |
assets/react/oidc-login.jsx.template | src/pages/Login.jsx | Login trigger (authorize.url() → redirect) |
assets/react/oidc-callback.jsx.template | src/pages/Callback.jsx | Code exchange handler (token.exchange via React component) |
assets/react/oidc-home.jsx.template | src/pages/Home.jsx | Home page with sign-in CTA and user info + sign-out |
Other frameworks: When their OIDC Templates directory is populated, follow the same pattern — read from the framework's assets/<framework>/ directory.
Common pitfalls (OIDC)
state mismatch: never construct the authorization URL manually. Always use client.authorize.url() — it generates and stores the state and code_verifier (PKCE) automatically. Calling token.exchange with a state that doesn't match stored state throws state_mismatch.
- Background renewal requires iframe allowance:
backgroundRenew: true opens a hidden iframe to the authorization endpoint. If your CSP blocks frame-src, renewal will silently fail. Add your PingOne/AIC domain to frame-src.
- PKCE is always on:
@forgerock/oidc-client uses PKCE by default and does not expose a toggle. Your OAuth client registration must have PKCE enabled (or set to optional).