diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..d36f35c --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,20 @@ +{ + "name": "ping-identity-skills", + "owner": { + "name": "Ping Identity", + "email": "developer-experience@pingidentity.com" + }, + "description": "Agent skills for Ping Identity platforms — six umbrella skills covering PingOne MT, PingOne Advanced Identity Cloud (ST), Ping Software Suite, DaVinci, universal services, app integration, and identity for AI.", + "plugins": [ + { + "name": "ping-identity", + "source": "./plugins/ping-identity", + "displayName": "Ping Identity", + "description": "Six umbrella skills modelled on the Cloudflare few-broad-skills approach: ping-quickstart, ping-foundation, ping-orchestration, ping-universal-services, ping-app-integration, ping-identity-for-ai. Each routes by intent, platform family, exact product/service, and reference tier.", + "version": "1.0.0", + "author": { "name": "Ping Identity" }, + "category": "identity", + "keywords": ["ping", "pingone", "aic", "davinci", "pingfederate", "pingaccess", "identity", "authentication", "ciam", "ai-identity"] + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..4474214 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Six-umbrella agent skill suite for Ping Identity platforms.", + "author": { "name": "Ping Identity" } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..d2e5388 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,21 @@ +{ + "permissions": { + "allow": [ + "mcp__plugin_polaris_glean__read_document", + "Bash(xargs sed *)", + "WebFetch(domain:docs.pingidentity.com)", + "mcp__plugin_polaris_glean__search", + "WebFetch(domain:docs.pingoneaic)", + "Bash(python3 *)", + "Skill(firecrawl:firecrawl-scrape)", + "WebFetch(domain:registry.npmjs.org)", + "WebFetch(domain:cdn-docs.pingidentity.com)", + "mcp__plugin_polaris_playwright__browser_navigate", + "WebFetch(domain:bedrock-runtime.eu-west-2.amazonaws.com)" + ] + }, + "sandbox": { + "enabled": true, + "autoAllowBashIfSandboxed": true + } +} diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json new file mode 100644 index 0000000..fb79bf0 --- /dev/null +++ b/.cursor-plugin/marketplace.json @@ -0,0 +1,11 @@ +{ + "name": "ping-identity-skills", + "owner": { "name": "Ping Identity" }, + "description": "Agent skills for Ping Identity platforms — six umbrella skills covering platform setup, orchestration, universal services, app integration, AI identity patterns, and platform routing.", + "plugins": [ + { + "name": "ping-identity", + "source": "./plugins/ping-identity" + } + ] +} diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json new file mode 100644 index 0000000..189ce34 --- /dev/null +++ b/.cursor-plugin/plugin.json @@ -0,0 +1,7 @@ +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Six-umbrella agent skill suite for Ping Identity: PingOne MT, PingOne ST (AIC), Ping Software Suite, DaVinci, universal services, app integration, identity for AI.", + "author": { "name": "Ping Identity" }, + "keywords": ["ping", "pingone", "aic", "davinci", "pingfederate", "identity", "authentication", "ciam", "ai-identity"] +} diff --git a/.github/workflows/build-reference-manifests.yml b/.github/workflows/build-reference-manifests.yml new file mode 100644 index 0000000..4010ee3 --- /dev/null +++ b/.github/workflows/build-reference-manifests.yml @@ -0,0 +1,73 @@ +name: Build Reference Manifests + +on: + push: + branches: [main] + paths: + - "plugins/ping-identity/skills/*/references/curated/**/*.md" + - "scripts/build_reference_manifests.py" + pull_request: + paths: + - "plugins/ping-identity/skills/*/references/curated/**/*.md" + - "scripts/build_reference_manifests.py" + workflow_dispatch: + +jobs: + build-manifests: + name: Build reference manifests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Build manifests + run: python3 scripts/build_reference_manifests.py --root . + + - name: Validate manifests against schema + run: | + pip install jsonschema --quiet + python3 - <<'PYEOF' + import json, sys + from pathlib import Path + from jsonschema import validate, ValidationError + + schema = json.loads(Path("shared/schemas/reference-manifest-schema.json").read_text()) + errors = [] + for manifest in Path("plugins").rglob("top-*.json"): + data = json.loads(manifest.read_text()) + # skip empty stubs + if data.get("docs") == [] and data.get("generated_at") is None: + continue + try: + validate(instance=data, schema=schema) + except ValidationError as e: + errors.append(f"{manifest}: {e.message}") + if errors: + for e in errors: + print(f"FAIL: {e}", file=sys.stderr) + sys.exit(1) + print(f"OK: {len(list(Path('plugins').rglob('top-*.json')))} manifest(s) validated") + PYEOF + + - name: Check for changes + id: diff + run: | + git diff --name-only plugins/*/skills/*/references/generated/ | head -20 + echo "changed=$(git diff --quiet plugins/*/skills/*/references/generated/ && echo false || echo true)" >> "$GITHUB_OUTPUT" + + - name: Commit updated manifests + if: steps.diff.outputs.changed == 'true' && github.ref == 'refs/heads/main' && github.event_name == 'push' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add plugins/*/skills/*/references/generated/top-*.json + git commit -m "chore(generated): rebuild reference manifests [skip ci]" + git push diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f7a6bd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.playwright-mcp/ diff --git a/.well-known/agent-skills/index.json b/.well-known/agent-skills/index.json new file mode 100644 index 0000000..1dc979a --- /dev/null +++ b/.well-known/agent-skills/index.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://agentskills.io/spec/v0.2.0/index.schema.json", + "version": "0.2.0", + "skills": [] +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..44c84d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,73 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +A **public-facing skill package** that exposes Ping Identity's agent skills through a unified layer. It is consumed by AI agents (Claude Code, Copilot CLI, Gemini CLI, etc.) to guide reasoning about Ping Identity platforms — not executed as application code. + +The repo ships one plugin: `plugins/ping-identity/`, which contains skills, reference files, and routing logic. The `/shared/` directory holds canonical taxonomies, schemas, templates, and evals that apply across all plugins. + +## No build system + +There are no build, compile, test, or lint commands. All content is Markdown and JSON. The only automated concern is frontmatter validation (CI rejects reference `.md` files with missing required fields). + +## Skill authoring + +Every skill lives in `plugins//skills//`: +- `SKILL.md` — routing logic only (≤120 lines); structured as: when to use → when NOT to use → multi-skill use cases → routing tables → retrieval escalation +- `ping-marketplace.json` — skill metadata for the Claude Code marketplace +- `references/curated/` — hand-authored task-completing docs (150–400 lines, `canonical: true`) +- `references/generated/` — CI-populated shortlists and stubs (`canonical: false`) + +Before writing any `SKILL.md` or reference file, read `shared/templates/AUTHORING-RULES.md` in full — it is the single source of truth for all authoring rules. Key constraints enforced in review: +- Every reference `.md` requires a complete frontmatter block (`title`, `product_family`, `capabilities`, `doc_type`, `status` are mandatory) +- `product_family` in frontmatter must match the file's directory path (e.g., a file in `pingone-st/` must have `product_family: pingone-st`) +- Curated anchors must include a `## Scope` section with explicit Covers/Does NOT cover statements +- No UI navigation steps ("click X", "navigate to Y") — write configuration facts, field tables, and decision rules instead +- Cross-references must use repo-relative paths, never absolute paths or bare filenames +- Plugin files (`plugins//`) must not reference anything in `/shared/` + +## Reference file structure (curated anchors) + +Follow this section order: +``` +# Title +One-sentence description. +## Scope +## [Content sections — tables, decision rules, constraints] +## Prerequisites +## Common variants +## Related references +## Source +``` + +## Routing architecture + +Skills route requests in three steps: +1. **Skill selection** — match user intent to the correct skill (`ping-quickstart`, `ping-foundation`, `ping-orchestration`, `ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`) +2. **Platform detection** — classify as `pingone-mt`, `pingone-st`, `ping-software`, or `cross-platform` +3. **Reference tier** — load curated anchors first (1–3 max); fall back to generated shortlist; only query external Docs MCP if both are insufficient + +Canonical routing rules: `shared/taxonomies/routing-rules.md`. Platform family definitions: `shared/taxonomies/platform-families.md`. Plugin-local routing (for standalone installs without `/shared/`): `plugins/ping-identity/routing-hints.md`. + +## Adding a new skill + +1. Create `plugins/ping-identity/skills//` with `SKILL.md` and `ping-marketplace.json` +2. The `name` field in `SKILL.md` frontmatter must exactly match the directory name +3. Add the skill to `plugins/ping-identity/plugin-map.md` and `plugins/ping-identity/references/index.json` +4. Write at least 3 benchmark prompts that should trigger the skill and 2 that should not — validate with `shared/evals/routing-eval.md` +5. Use `shared/templates/SKILL.template.md` as the starting structure + +## Eval format + +Run skill routing evals using the format in `shared/evals/routing-eval.md`. The scorecard has five dimensions: Routing Correctness (30), Context Correctness (25), Answer Correctness (20), Token Efficiency (15), Fallback Discipline (10). A run fails at <80/100 or any hard-gate violation (wrong umbrella skill, major factual error, unnecessary external docs call, token spend >150% of minimum). + +## Plugin standalone vs. full-repo + +The plugin at `plugins/ping-identity/` is designed to work standalone (without `/shared/`). In that mode it uses its own orientation files (`README.md`, `plugin-map.md`, `platform-scope.md`, `routing-hints.md`). When the full repo is present, `/shared/taxonomies/` is authoritative and overrides plugin-local equivalents. + +## GitHub + +Remote: `https://github.com/brando-dill_pingcorp/agent-skills.git` +Use the `george-bafaloukas_pingcorp` EMU account for all `gh` operations (`gh auth switch --user george-bafaloukas_pingcorp`). diff --git a/HELIX-SETUP.md b/HELIX-SETUP.md new file mode 100644 index 0000000..35798c7 --- /dev/null +++ b/HELIX-SETUP.md @@ -0,0 +1,507 @@ +# Helix Setup Guide — AIC and DaVinci + +> Compiled from internal Ping Identity documentation (Glean-indexed). Source URLs cited inline. Some sections (notably DaVinci validation/apply gates) had only partial documentation available; those are flagged explicitly. + +--- + +## 1. Helix Overview + +Helix is Ping's low-code AI Agent platform, originally released as the "Helix Playground" (formerly "Everest") in Q4 2024 and now being ported into PingOne. It exposes REST APIs for building, publishing, and invoking AI agents. Agents are graph-based (DAG of nodes) and consist of inputs (Text/File), AI Tasks, Functions, AI/Function Decisions, Vector Search, and Output nodes. + +Key concepts ([Helix Playground Release](https://docs.google.com/document/d/1VSUvZO14KG5jUfx8ZLJsXWR7YTNmo3X5KDWNkBu1QPk), [Helix Security Guide](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU)): + +- **Tenant / Environment** — multi-tenant unit; each PingOne or AIC environment can map 1:1 to a Helix environment. +- **IDP** — every Helix environment is bound to an IDP (AIC, PingOne, or external). The IDP governs agent identity lifecycle, inbound auth, and outbound auth. +- **Agent** — a published unit identified by `(env_id, name, version)`. Draft → Published lifecycle, multiple published versions allowed for A/B and rollout. +- **Conversation** — message container per session; channels carry messages between user ↔ agent (default channel is auto-created). +- **Channel** — communication path. Simple agents use a single auto-generated User↔Agent channel. +- **Message** — has `class` (`start`, `wait`, `complete`), `content` (input field map), `field_id`, `value`. Async by default; LLM calls are long-running. +- **Tools** — registered Python functions or HTTP/REST calls invocable from AI Task nodes. Tools declare credential aliases. +- **Credentials** — four types: `Service Account JWK`, `OAuth Client`, `API Key`, `CIBA`. Stored per-agent. +- **`get_security_headers()`** — Helix runtime helper that returns the `Authorization` (or custom) header for outbound calls. + +Hierarchy: `Agent → Conversation → Channel → Messages` ([Agent Interaction Model](https://docs.google.com/document/d/1RTXcH4Mit_WCSBGotUMNA68Zm9W6gX0DDXJmdzfIwjA)). + +Public landing: https://www.pingidentity.com/en/lp/ac/pinghelix.html +Helix Playground UI: https://openam-helix.forgeblocks.com/dpc + +--- + +## 2. Prerequisites + +- **Helix tenancy** — provisioned by the Helix team. Internally requested in `#helix-playground` Slack ([Helix Playground Release](https://docs.google.com/document/d/1VSUvZO14KG5jUfx8ZLJsXWR7YTNmo3X5KDWNkBu1QPk)). +- **Helix role** — one of `Data Admin`, `Data Author`, `Data Viewer`. Only Data Admins can create API keys and invite users. +- **An IDP**: + - **AIC**: an AIC tenant (e.g. `*.forgeblocks.com`) with an OAuth 2.0 client. DCR (Dynamic Client Registration) recommended so agent identities are created automatically. + - **PingOne**: a worker app in the target environment with roles `Client Application Developer` (for DCR-style agent identity creation) and `Configuration Read Only` at organization level. +- **Helix environment ID** — UUID assigned at tenant creation; required in every API path. +- **Authentication credential**: API Key (`x-api-key` header) OR a Bearer access token from the onboarded IDP. +- **For headless invocation**: a Published agent version (drafts can also be invoked, but tokens via API key only target published versions). + +Two API base URLs referenced internally: +- `https://openam-helix.forgeblocks.com/dpc/jas/helix/v1` — playground / AIC-hosted Helix +- The same path mounted behind `helix-proxy` inside PingOne (see §4.1) + +Sources: [Helix-AIC Integration](https://docs.google.com/document/d/1fwQ8ZjCVNzTr2BFtl2HHvuEFwjSxkbgx20igl97jlGQ), [Helix Security Guide](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU). + +--- + +## 3. Setting up Helix with PingOne AIC + +### 3.1 Configure AIC as IdP for Helix + +(From [Helix Security Guide §IDP onboarding for AIC](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU) and [Helix-AIC Integration](https://docs.google.com/document/d/1fwQ8ZjCVNzTr2BFtl2HHvuEFwjSxkbgx20igl97jlGQ).) + +1. In the AIC admin UI, create an OAuth 2.0 Client in the alpha realm with: + - `client_id` and `client_secret` (the doc demo uses `helix-idp-demo` for both). + - **Grant types**: at minimum `Client Credentials`. Add `Resource Owner Password Credentials` if you want user-token issuance against this client. + - **Scopes**: `openid`, `profile` (plus product-specific scopes such as `fr:idm:*`, `fr:iga:*`). + - DCR scope enabled if you want Helix to auto-create agent identities. +2. Switch to AIC **Native Consoles → Access Management**, select the alpha realm, then **Services → OAuth2 Provider → Advanced**, and set `OAuth2 Token Signing Algorithm = RS256` (so Helix can validate tokens via JWKS URI). +3. In Helix UI, go to **Admin → Identity Provider** and enter the AIC client details (issuer, JWKS URI, client_id, client_secret, token endpoint). +4. Save. The Helix environment now uses AIC for inbound auth and agent-identity creation. + +**Verify**: Create a draft agent. If DCR was enabled, an OAuth client named `__agent` (e.g. `32ea2671_2337_4f50_bcfb_693c0072672a_DescribePlace_agent`) appears in AIC's OAuth Clients section. + +### 3.2 Configure agent identity / OAuth client credentials + +Two paths (Security Guide §Agent Identity, §Manually create OAuth Client): + +**Auto (DCR)**: If the IDP onboarding client has DCR scope, every new Helix agent automatically receives an OAuth client in AIC. No manual work. + +**Manual**: +1. In AIC, create a Custom Application → OIDC OpenId Connect → Service. +2. Set name/owner; assign client_id and client_secret. +3. On the **Sign On** tab: Grant Types = `Client Credentials`; Scopes = `openid profile` (+ any product scopes). +4. In Helix UI, click the three-dot menu next to the agent → **Agent Identity** → enter `client_id` and `client_secret`. + +**Agent credentials (separate from agent identity)** — used by tools/functions for outbound calls. Four types via Helix UI (three-dot menu → **Credentials**) or REST API: + +``` +POST https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments//agents//credentials +Authorization: +Content-Type: application/json +{ + "credential_name": "iga-oauth", + "credential": { + "type": "oauth", + "client_id": "", + "client_secret": "", + "token_endpoint": "", + "scope": "fr:iga:*" + } +} +``` + +Notes: +- For OAuth: `grant_type` is hard-coded to `client_credentials`. +- For Service Account: uses `urn:ietf:params:oauth:grant-type:jwt-bearer`. +- For OAuth-via-AIC, the example agent (`AICIGAAgentUsingOAuthCredentials`) requires creating an Auth Script and enabling **OAuth Provider Overrides → Access Token Modification Plugin Type = SCRIPTED** in AIC. + +### 3.3 Author custom tools for AIC REST + +Tools are Python functions registered in Helix. They use credential aliases that the consuming agent must satisfy at runtime. + +**Pattern A — Function inside an agent (inline)**: + +```python +def function0b138036f3d7(textInput9999aa5e0489: str): + import requests + from helix.security import get_security_headers + url = f"https://{baseUrl}/openidm/managed/alpha_user?_queryFilter=mail eq '{textInput9999aa5e0489}'" + headers = get_security_headers( + credential_name='am_users', + scope='fr:idm:*', + grant_type='urn:ietf:params:oauth:grant-type:jwt-bearer' + ) + headers['accept'] = 'application/json' + return requests.get(url, headers=headers).json() +``` + +**Pattern B — Reusable custom tool (preferred)** — registered separately under **Tools** in Helix Studio, given an alias. The using agent satisfies the alias via Tool Credentials at agent config time. + +```python +def get_aic_user_by_filter(email: str): + import requests + from helix.security import get_security_headers + url = f"https://{baseUrl}/openidm/managed/alpha_user?_queryFilter=mail eq '{email}'&_fields=userName,givenName,sn,mail" + headers = get_security_headers( + credential_name=am_users, # NOTE: unquoted alias + scope='fr:idm:*', + grant_type='urn:ietf:params:oauth:grant-type:jwt-bearer' + ) + headers['accept'] = 'application/json' + r = requests.get(url, headers=headers) + r.raise_for_status() + return r.json() +``` + +**`get_security_headers()` reference** (Security Guide §Integration: Get Security Headers in Tools and Functions): + +```python +def get_security_headers( + credential_name=None, # alias name, unquoted when used inside a tool + scope=None, # required for OAuth/SA + grant_type=None, # client_credentials | urn:ietf:params:oauth:grant-type:jwt-bearer + use_existing_token=False, # token passthrough — no other params needed + custom_header=None, # default 'Authorization'; e.g. 'API_KEY' + use_agent_token=False # use agent identity instead of credential +): ... +``` + +Returns: `{'Authorization': 'Bearer ey…'}` or `{'': '...'}`. + +**Tool registration UI flow**: Tools section → New Tool → declare parameters, add Python script. Define aliases inline; tools become visible in Agent's Tool Credentials tab where the agent author binds each alias to a real credential (created on the fly with the `+` button). + +End-to-end examples: Security Guide sections **Agents → P1 User info using custom tools** (PingOne) and **AGENT → AIC User info using custom tools** (`GetAICUser`, `GetAICUserByFilter`). + +### 3.4 Headless conversation invocation + +Three forms of inbound auth ([Helix Security Guide §Inbound Access](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU)): + +1. **Helix API key** (`x-api-key: `) — created by Data Admin in **Admin → API Keys**. Scoped to tenancy + agent IDs. Recommended for service-to-service headless invocation. +2. **AIC end-user / client OAuth bearer** (`Authorization: Bearer ...`). +3. **Helix user token** copied from the Helix UI's network call (development only). + +**Get an AIC bearer (client_credentials)**: + +```bash +curl -X POST 'https://openam-qa-helix-testimg.forgeblocks.com/am/oauth2/alpha/access_token' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -H 'Authorization: Basic ' \ + -d 'scope=openid profile' \ + -d 'grant_type=client_credentials' +# → { "access_token": "ey...", "token_type": "Bearer", "expires_in": 3599 } +``` + +**Conversation lifecycle** (concrete URLs from [Helix-AIC Integration](https://docs.google.com/document/d/1fwQ8ZjCVNzTr2BFtl2HHvuEFwjSxkbgx20igl97jlGQ) and [Agent Interaction Model](https://docs.google.com/document/d/1RTXcH4Mit_WCSBGotUMNA68Zm9W6gX0DDXJmdzfIwjA)): + +**Step 1 — Create a conversation**: + +```bash +curl -X POST \ + 'https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments//agents//conversations' \ + -H 'Authorization: Bearer ey...' \ + -H 'Content-Type: application/json' \ + --data '{ + "name": "conversation_2805957c-a6a1-40ee-bd00-fc238c6fba64", + "agent": { "version": "0.08" } + }' +``` + +Response: +```json +{ + "created_on": "2025-01-24T19:30:48Z", + "home_channel": "500e0225-78fe-45c5-a5f7-f5664b5a2a5e", + "id": "62cbdcaf-115e-4edd-a311-105ce6724fdb", + "name": "conversation_e42f4da6-...", + "owner": "e51fa13c-c536-4d60-b9cc-81a84c4214eb" +} +``` + +**Step 2 — Post a START message** (to `home_channel`). Sync vs async selected by Content-Type: + +- Sync: `Content-Type: application/json` +- Async: `Content-Type: application/json; async=true` + +```bash +curl -X POST \ + 'https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments//conversations//channels//messages' \ + -H 'Authorization: Bearer ey...' \ + -H 'Content-Type: application/json; async=true' \ + --data '{ + "class": "start", + "content": { + "textInputbc96883ff5d3": "Melbourne" + } + }' +``` + +Sync response: +```json +{ + "message_class": "complete", + "conversation_id": "...", + "message_id": "...", + "channel_id": "...", + "content": [{ + "class": "complete", + "field_id": "task384d0d61d94e", + "type": "text", + "value": "Melbourne is a dynamic and cosmopolitan city..." + }] +} +``` + +Async response: +```json +{ "message_class": "wait", "conversation_id": "...", "message_id": "078fba75-...", "channel_id": "..." } +``` + +**Step 3 — Poll the channel for completes** (async only): + +```bash +curl 'https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments//conversations//channels//messages' \ + -H 'Authorization: Bearer ey...' +``` + +Loop until you see a message with `sender_role: "agent"`, `class: "complete"`, and `initiator_message_id == `. Structured agent output is in `value` (typically JSON when the agent uses an output node). + +**Step 4 — (Optional) Inspect run trace**: + +```bash +GET https://openam-helix.forgeblocks.com/dpc/jas/helix/internal/v1/environments//conversations//channels//messages//runs +``` + +Returns full execution trace including each node's prompt stack, model metrics, and intermediate outputs. + +**Step 5 — Close** — Helix manages cleanup; no explicit close API documented. Conversations are not currently expected to be re-opened by users; automatic cleanup policy is TBD ([Agent Interaction Model](https://docs.google.com/document/d/1RTXcH4Mit_WCSBGotUMNA68Zm9W6gX0DDXJmdzfIwjA)). + +**Important quirk**: When Helix returns the polled message list, a START message with `n` inputs is *expanded into `n` individual messages* on the channel, all sharing the same `message_id` but different `field_id`. Documented as confusing; may change before GA. + +### 3.5 Token passthrough setup + +Use case: forward the inbound user's token into outbound calls so downstream APIs enforce that user's permissions ([Helix Security Guide §Token Passthrough](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU)). + +When to use: +- Calling APIs that don't support standard OAuth client_credentials (notably DaVinci endpoints). +- Need user-scoped authorization in the agent's outbound calls. +- Cross-environment scenarios where agent identity wouldn't have the right perms. + +Setup: no per-agent credential to create. The function opts into passthrough: + +```python +def functionedfc6bf898a7(textInputf89a326d1a9c: str): + import requests + from helix import security + headers = security.get_security_headers(use_existing_token=True) + headers['Accept'] = 'application/json' + return requests.get("https://openam-helix.forgeblocks.com/dpc/jas/tenants", + headers=headers).text +``` + +Sample agent: **TokenPassthrough** in `helix-security` (Env Id `a9c33d49-52a9-4596-b978-9dd165f719fc`). + +Known gotchas (from `#helix-p1-auth-design` Slack): +- Inside PingOne, the access token `helix-proxy` receives is an *internal* token. It is re-signed before being passed downstream; the issuer is preserved so the API Gateway does not block it. +- Cross-environment passthrough requires both the user and agent identities to exist in the same environment (open issue at the time of writing). + +--- + +## 4. Setting up Helix with PingOne DaVinci + +Internal documentation is significantly thinner here than for AIC. The DaVinci-Helix integration is being delivered under JIRA **DV-22278** (`P1DAV-E-691`, Gartner-2026 release Q2 2026) and the metrics-Helix POC is closed under **P1ME-3460**. The following is what is documented. + +### 4.1 Helix-backed flow generation pattern (architecture) + +(From [What and Why PingOne AI Assistant Service](https://docs.google.com/document/d/1H_fLBCGNmDwBd7k2ls17JyfLv9sZIbg6rel5suJTz-M) and [Helix Proxy Claude TM](https://pingidentity.atlassian.net/wiki/spaces/SE/pages/2875555922).) + +The DaVinci AI assistant calls Helix through `helix-proxy`, a Java 21 / Spring Boot 3.5 microservice that lives **inside** the PingOne trust boundary: + +``` +PingOne Console UI → API Gateway → helix-proxy → helix-platform (Helix) → LLM +``` + +`helix-proxy` is responsible for: +- Conversation / message proxying with OAuth2 JWT enforcement (permissions: `p1ai:create:conversations`, `p1ai:read:conversations`, `p1ai:create:messages`, `p1ai:read:messages`). +- Environment onboarding / offboarding via Kafka events (`ENVIRONMENT.CREATED`, `ENVIRONMENT.DELETED`) — automatically creates a Helix tenant, a PingOne worker app, an environment API key, and an IDP config per eligible PingOne environment. +- Audit event publishing to the `aidp-helix-audit-events` Kafka topic. +- Dynamic URL routing between standard and testing Helix endpoints via `HELIX_TESTING_FEATURE_FLAG` (`HelixUrlResolver`). + +A DaVinci-specific proxy hop (`davinci-proxy → helix-proxy → helix-platform → LLM`) was discussed but as of June 2025 is debated; the team is questioning whether the additional hop is necessary (`#davinci-ai-poc` Slack thread, June 2025). + +The DaVinci agent itself is small (<1 MB) compared to the PingOne Assistant agent (>5 MB), making Helix conversation creation faster against it (`#helix-bringing-to-p1`, April 2026). + +### 4.2 Conversation lifecycle for DaVinci agents + +API shape is identical to §3.4 — same paths, same body, same async semantics — but routed through `helix-proxy` instead of directly to `openam-helix.forgeblocks.com`. From a Slack-shared real call (jrawat, April 2026): + +```bash +curl --location \ + '/v1/environments//agents/PingOneAgent/conversations' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data '{ "name": "PingOneAgent Conversation", "agent": { "version": "draft" } }' +``` + +The PingOne Assistant currently sends the **signed-in user's access token** to `helix-proxy` (not a worker app), and Helix performs token-passthrough authorization. Cross-environment scenarios (admin in env A, agent in env B) are a known limitation; a worker-app-with-client-credentials approach was being brainstormed as of mid-2025. + +### 4.3 Authoring DaVinci-facing custom tools + +Same Tools/Function pattern as §3.3, but the credential type for DaVinci is **passthrough** (DaVinci does not support standard OAuth, per [Helix Playground Release](https://docs.google.com/document/d/1VSUvZO14KG5jUfx8ZLJsXWR7YTNmo3X5KDWNkBu1QPk) §Agent Credential): + +```python +from helix import security +import requests + +def list_dv_flows(): + headers = security.get_security_headers(use_existing_token=True) + headers['Accept'] = 'application/json' + return requests.get( + f"https://api.pingone.com/v1/environments/{envId}/davinci/flows", + headers=headers + ).json() +``` + +For DaVinci Metrics specifically, the integration is via a new DV Metrics API designed to accept structured queries from a Helix agent (epic [P1ME-3460](https://pingidentity.atlassian.net/browse/P1ME-3460) — closed POC stage). The Metrics API uses internal IAM roles for write paths; Helix only reads. + +### 4.4 Validation, versioning, and apply gates for generated flows + +**No internal documentation found via Glean search that specifies the validation/versioning/apply gates for the Helix→DaVinci flow-generation use case** (request shape, returned flow JSON schema, validate-then-apply gate, version reconciliation). Internal threads ([DV-22278](https://pingidentity.atlassian.net/browse/DV-22278), `DaVinci-Helix` label) reference this work but the searchable artefacts are JIRA epics and threat models, not engineering specs. The threat model ([WIP - Helix Runtime in PingOne Threat Model](https://pingidentity.atlassian.net/wiki/spaces/SE/pages/2714697742)) confirms a "DV Assistant (running on Helix services deployed in PingOne)" was slated for May 8, 2026 (Gartner magic quadrant review release), but the request/response contract for `POST flow context → return flow JSON → validate → apply` is not in any indexed doc. + +**This section needs to be authored from product team input** — recommended contacts (from Slack history): Sourav Chakraborty (DV-22278 assignee), Satish Varagani (DV-helix-proxy), Mayank Somani (helix-proxy), Raminder Kaler (Helix platform). + +--- + +## 5. Authoring and Publishing Agent Versions + +(From [Helix Playground Release §Developing Agents](https://docs.google.com/document/d/1VSUvZO14KG5jUfx8ZLJsXWR7YTNmo3X5KDWNkBu1QPk).) + +Identifier: `(env_id, name, version)`. Within an environment you can have: +- At most **one DRAFT** version per name. +- At most **one PUBLISHED** version per name+version (multiple published versions allowed across different version strings). + +Authoring options: +- **UI**: Agent Builder canvas — drag Text Input / File Input / AI Task / Function / Vector Search / AI Decision / Function Decision / Output nodes. +- **API**: `POST /environments//agents/` with the full agent JSON (Security Guide includes a complete example for the `describePlaceDemo` agent — `entities`, `ui_config`, `tools`, `tables`, `files`, `version`, `name`). + +List endpoints: +```bash +GET .../environments//agents?offset=0&limit=10&state=draft +GET .../environments//agents?offset=0&limit=10&state=published&latest=true +GET .../environments//tools?offset=0&limit=10&type=custom +``` + +Publish flow: +- In Studio, mark draft as Published; assign a unique version string (no clash with existing published). +- The `version` is referenced in conversation create body (`"agent": { "version": "0.08" }`) when invoking. + +Helix API Keys are scoped to `(tenancy, agentIDs)` and required for service-to-service invocation of published agents. Created by Data Admins via Admin → API Keys; **download/copy the key value at creation time** (it is never retrievable later). + +CI/CD for agents (Q1 FRAaS epic, design TBD): +- **Agent Bundle** packages agent + tool + config + file + vector-store dependencies. +- **Security Bundle** captures credential provisioning required before the agent is functional. + +--- + +## 6. Sample End-to-End Flows + +### 6.1 AIC: Add MFA journey via Helix + +End-to-end pattern combining §3: + +1. Onboard AIC as IDP for the Helix env (§3.1). +2. Create an AIC service-account credential `am_users` with scope `fr:idm:*`, grant_type `urn:ietf:params:oauth:grant-type:jwt-bearer` (§3.2). +3. Author two custom tools backed by AIC IDM and AM REST: + - `GetJourneyByName(realm, name) → Journey` (AM endpoint `/am/json/realms/root/realms/{realm}/realm-config/authentication/authenticationtrees/trees/{name}`). + - `UpdateJourney(realm, name, body)` (PUT to same path with `Accept-API-Version: resource=2.0`). +4. Build an agent **MfaJourneyEditor** with inputs `realm`, `journeyName`, `instruction` and a single AI Task node that uses both tools. +5. Publish version `0.1`. Generate an API key. +6. Headlessly invoke: + +```bash +TOKEN="$(./get-aic-token.sh)" + +CONV=$(curl -s -X POST \ + "https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments/$ENV_ID/agents/MfaJourneyEditor/conversations" \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"name":"add-mfa","agent":{"version":"0.1"}}') + +CID=$(jq -r .id <<<"$CONV"); CHAN=$(jq -r .home_channel <<<"$CONV") + +curl -s -X POST \ + "https://openam-helix.forgeblocks.com/dpc/jas/helix/v1/environments/$ENV_ID/conversations/$CID/channels/$CHAN/messages" \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json; async=true' \ + -d '{"class":"start","content":{ + "textInputRealm":"alpha", + "textInputJourneyName":"Login", + "textInputInstruction":"Insert a Push MFA node after Identity Store Decision." + }}' +# → poll GET .../channels/$CHAN/messages until class=complete & sender_role=agent +``` + +The agent calls AM via `get_security_headers(credential_name=am_users, scope='fr:idm:*', grant_type='urn:ietf:params:oauth:grant-type:jwt-bearer')` and writes back the modified Journey JSON. + +### 6.2 DaVinci: Generate a flow via Helix + +Conceptual pattern (note caveat in §4.4 — exact contract not in internal docs): + +1. PingOne Console UI gathers user prompt + current flow context (env_id, flow_id, flow JSON) and sends signed-in admin's access token. +2. UI calls `helix-proxy` `/v1/environments//agents/DaVinciAssistant/conversations` (Bearer = admin token). +3. Posts `start` message: `content` includes `textInputUserPrompt`, `textInputFlowContext`, `textInputEnvironment`. +4. Helix agent uses passthrough credential to call DV admin APIs and an LLM to synthesize new flow JSON. +5. UI polls for `complete`, parses returned JSON, presents diff/preview to admin. +6. UI calls DaVinci's own Apply API (not Helix) to commit the flow — Helix is purely the generation/troubleshoot engine. + +Sample shape of the create-conversation call (April 2026 Slack snippet): + +```json +POST /v1/environments//agents/DaVinciAssistant/conversations +Authorization: Bearer +{ + "name": "DV Assistant Conversation", + "agent": { "version": "draft" } +} +``` + +The DaVinci agent definition itself, the input schema, and the validation/apply gates are not in indexed docs. + +--- + +## 7. Known Limitations and Gotchas + +From Slack threads and threat models: + +- **Inbound errors aren't propagated**: the current Helix MVP "does not capture and report internal errors that happen during an agent invocation"; clients must implement timeouts and surface generic error states ([Agent Interaction Model](https://docs.google.com/document/d/1RTXcH4Mit_WCSBGotUMNA68Zm9W6gX0DDXJmdzfIwjA)). +- **Start-message expansion**: a START with N inputs is reflected as N separate messages on the channel sharing one `message_id` — confusing to consumers, may change before GA. +- **Inputs are addressed by generated UUID** (e.g. `textInputbc96883ff5d3`), not by friendly name. Ties UI tightly to a specific agent version. New version → new UIDs → UI updates. +- **PingOne does not support DCR**: agent identity creation requires manual OAuth client provisioning unless `Client Application Developer` role is assigned. **Always assign at least one role** to the worker app or no access tokens will be issued. +- **AIC token-signing** must be RS256 (asymmetric) for JWKS-based validation to work. +- **DaVinci does not support standard OAuth**; outbound calls from a Helix agent into DV must use **token passthrough** (`use_existing_token=True`). +- **Internal access tokens** received by `helix-proxy` are not externally usable — re-signed by `helix-proxy` before forwarding so the API Gateway accepts them. +- **PingOne Assistant agent is >5 MB**, which slowed conversation creation (S3 fetch of agent definition). DaVinci agent is <1 MB and recommended for benchmarking. +- **Cross-environment** PingOne Assistant calls fail when admin is in env A and agent in env B; mitigation under design. +- **`credential_name` inside a tool must be UNQUOTED** (e.g. `worker_user`, not `'worker_user'`) — this is an alias reference, not a string. +- **Sync vs async**: sync requests can hit gateway timeouts on long LLM calls; the Helix team recommends async (`Content-Type: application/json; async=true`) and polling. +- **Admin API keys** can only delete environments and create env-API-keys for environments they themselves created — created by super admins via `#helix-playground` ([Security Guide §Admin API Keys](https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU)). +- **Helix Roles**: only Data Admin can create API keys and invite users; Data Author and Data Viewer have reduced perms. +- **Agent token endpoint**: the token endpoint Helix uses for agent identity is whatever was supplied during IDP onboarding — not configurable per-agent. + +--- + +## 8. Source Documents + +Primary specs (internal, Glean-indexed): + +- **Helix Security Guide 1.0** — owner Raminder Kaler — https://docs.google.com/document/d/1Ij0kkgnXIInDHdIJVS4Hnmau9aG-1RosxUkEwYAcfEU — **the authoritative setup doc**: IDP onboarding, agent identity, agent credentials, `get_security_headers`, sample agents, API keys, token passthrough. +- **Helix Playground Release Notes** — owner Sudhakar Peddibhotla — https://docs.google.com/document/d/1VSUvZO14KG5jUfx8ZLJsXWR7YTNmo3X5KDWNkBu1QPk — concept overview, node types, REST API & Postman pointers, FAQ. +- **Helix-AIC Integration** — owner Andersson Garcia — https://docs.google.com/document/d/1fwQ8ZjCVNzTr2BFtl2HHvuEFwjSxkbgx20igl97jlGQ — concrete curl walkthrough of IDP onboarding through to async conversation + run trace. +- **Journey AI Agent Interaction Model** — owner Dave Ernsting — https://docs.google.com/document/d/1RTXcH4Mit_WCSBGotUMNA68Zm9W6gX0DDXJmdzfIwjA — full Agent → Conversation → Channel → Message lifecycle, request/response shapes, JSON Schema. +- **What and Why PingOne AI Assistant Service** — owner Vasuki Dileep — https://docs.google.com/document/d/1H_fLBCGNmDwBd7k2ls17JyfLv9sZIbg6rel5suJTz-M — `helix-proxy` architecture, sequence diagrams. +- **Helix Proxy Claude TM** — Confluence — https://pingidentity.atlassian.net/wiki/spaces/SE/pages/2875555922 — service summary, tech stack, OAuth permission scheme. +- **Helix Playground Threat Model** — Confluence — https://pingidentity.atlassian.net/wiki/spaces/SE/pages/1167622145 +- **PingOne AI Assistant Threat Model** — Confluence — https://pingidentity.atlassian.net/wiki/spaces/SE/pages/1937408023 +- **WIP - Helix Runtime in PingOne Threat Model** — Confluence — https://pingidentity.atlassian.net/wiki/spaces/SE/pages/2714697742 +- **P1ME-3460 DaVinci Metrics Helix API Threat Model** — Confluence — https://pingidentity.atlassian.net/wiki/spaces/SE/pages/2008121454 + +Helix MVP API artefacts (referenced from Journey AI doc): +- Helix MVP API Google Drive — https://drive.google.com/drive/folders/1HaXE0Omlm-g9d61KYA7-bMb_2zA5u_zz +- HTML Render of API Specification — https://drive.google.com/file/d/1Mst3JcLozk9WhJI-vIm_qMDEwv_HpH5s/view +- Helix REST APIs (transactional + security) — https://drive.google.com/file/d/1v2t-BgfmuuOKt6FsHfCYixopcbhZIIx3/view +- Helix Agent Evaluation APIs — https://drive.google.com/open?id=1x2sgSDmbRPd3UKHbhKLFAIRzqPPGyVoD +- Postman collection — https://drive.google.com/open?id=1GI8j1psHaALlaeuM-4BjZL50yuHedC7- + +JIRA epics: +- DV-22278 (DaVinci-Helix flow generation, Q2 2026) — https://pingidentity.atlassian.net/browse/DV-22278 +- P1ME-3460 (DaVinci Metrics API – Helix Integration MVP, closed) — https://pingidentity.atlassian.net/browse/P1ME-3460 +- P1AX-3589 (PingOne Helix Integration Service Backend Proxy, closed) — https://pingidentity.atlassian.net/browse/P1AX-3589 +- FRAAS-21880 (AIC feature for Running Helix Agents, closed) — https://pingidentity.atlassian.net/browse/FRAAS-21880 +- AI-2530 (OOTB report for Helix Token Usage By Model) — https://pingidentity.atlassian.net/browse/AI-2530 +- AI-2128 (Assisting PingOne assistant agent) — https://pingidentity.atlassian.net/browse/AI-2128 + +Slack channels for live questions: +- `#helix-playground` — main support channel; super-admin API key requests go here. +- `#helix-p1-auth-design` — token passthrough / cross-env auth design. +- `#helix-bringing-to-p1` — current PingOne integration coordination. +- `#davinci-ai-poc` — DV-Helix integration discussion. diff --git a/LICENSE b/LICENSE index 9b2d5b3..269570a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Ping Identity +Copyright (c) 2026 Brando Dill Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PHASE-1-EXECUTION-PLAN.md b/PHASE-1-EXECUTION-PLAN.md new file mode 100644 index 0000000..1838f0f --- /dev/null +++ b/PHASE-1-EXECUTION-PLAN.md @@ -0,0 +1,363 @@ +# Phase 1 — Detailed Execution Plan + +> Companion to [PLAN.md](PLAN.md) § "Phase 1 — Author the three planned umbrella skills". Decomposes the four-day window (May 31 – June 3) into sequenced sub-phases with concrete deliverables, file lists, dependencies, and exit gates. + +## Phase 1 goal (verbatim from PLAN.md) + +`ping-universal-services`, `ping-app-integration`, and `ping-identity-for-ai` ship at **orientation depth** — each with a working `SKILL.md` decision tree and **≥3 curated anchors**, complete eval prompt sets, and a passing Layer 1 routing run. + +## Entry preconditions (must be true before Phase 1 starts) + +| # | Precondition | How to verify | Status | +|---|---|---|---| +| 1 | Phase 0 exit criteria met | `python -m evals.harness.validate_prompts` exits 0; `python -m evals.harness.run_eval --adapter mock --layer 1` exits 0 | ✅ per `evals/README.md` ("Phase 0 smoke check passes") | +| 2 | All six skill directories exist with the per-skill tree | `ls plugins/ping-identity/skills//references/{curated,generated,runtime}/` returns 0 in all 6 | ✅ confirmed | +| 3 | Three planned skills have SKILL.md + ping-marketplace.json scaffolds | File exists check | ✅ confirmed | +| 4 | Existing prompt sets exist for the three planned skills | `evals/prompts/ping-{universal-services,app-integration,identity-for-ai}.yaml` exist | ✅ stubs present (~1.3–1.4 KB each — need expansion to spec) | +| 5 | `rules/routing-rules.md` is committed and authoritative | File exists, references all 6 skills | ⚠️ verify in 1.0 | +| 6 | Decision needed: Docs MCP availability for `references/runtime/` (PLAN.md decision #4) | Product confirms public docs site fallback is the v1 path | ⚠️ assume public docs fallback unless told otherwise | + +If any precondition fails, address before starting 1.1. + +--- + +## Sub-phase 1.0 — Prep & alignment (Day 0, ~2 h) + +**Goal:** clear ambiguities, lock the curated-anchor outline for each skill, and confirm the eval pass bar so authoring doesn't loop on rework. + +### Steps + +1. **Read current state.** Open the three planned skills' `SKILL.md` files; capture the existing routing tables and which curated anchor filenames they already promise (the universal-services SKILL.md, for example, already references `references/curated/.md` placeholders). Treat any name in the existing routing table as a hard contract — anchors must be authored at those exact paths or the SKILL.md must be edited in the same PR. +2. **Verify `rules/routing-rules.md`** lists all 6 skills with disambiguation rules. If gaps exist, fix in this sub-phase before Phase 1.1 — multi-skill composition cases for the 3 planned skills must be documented before authoring. +3. **Lock anchor outlines.** For each skill, draft a one-paragraph outline per curated anchor (filename → 3-bullet "Covers / Does NOT cover / Decision rules" outline). Save the three outlines as a single working note (`PHASE-1-OUTLINES.md`, untracked or PR-local) — this is the spec the authoring sub-phases write against. +4. **Confirm `shared/templates/curated-reference.template.md` and `shared/templates/AUTHORING-RULES.md`** are the source of truth (per CLAUDE.md). If either template is missing required sections (Scope, Prerequisites, Common variants, Related references, Source), patch first. +5. **Pick the Layer 1 adapter.** Phase 0 uses `mock`; Phase 1 needs the `claude` adapter wired up against the real API. Confirm API keys are present in the environment used for `python -m evals.harness.run_eval --adapter claude --layer 1`. If not, file a blocker before 1.4. + +### Deliverables + +- `PHASE-1-OUTLINES.md` (working doc) — anchor-by-anchor outlines for all three skills. +- Patched `rules/routing-rules.md` if gaps were found. +- Confirmed `claude` adapter readiness (API key + smoke test on a single prompt). + +### Exit gate + +- All three planned-skill SKILL.md files cross-checked against PLAN.md § Phase 1 step 1–3 anchor lists. Any naming mismatches resolved (either fix the SKILL.md routing table or rename the planned anchor file). +- One smoke prompt round-trips through `run_eval --adapter claude --layer 1`. + +--- + +## Sub-phase 1.1 — `ping-universal-services` content (Day 1, ~1 day) + +**Goal:** ship orientation-depth curated content + complete eval prompt set. + +### Curated anchors (PLAN.md § Phase 1 step 1) + +Author all four under `plugins/ping-identity/skills/ping-universal-services/references/curated/`: + +1. `universal-services-overview.md` — what the universal services umbrella covers (Protect, Verify, Credentials, IGA, SSO, Authorize), the "consumed-from-multiple-platforms" criterion, when to reach for this skill vs. `ping-foundation` or `ping-orchestration`. +2. `choosing-the-right-service.md` — decision table: user intent → which service. Must cover the six services named in PLAN.md and the SKILL.md routing table. +3. `service-invocation-patterns.md` — how a service is invoked from PingOne MT vs. PingOne ST vs. Ping Software, including DaVinci-flow integration and the policy/verification handoff pattern. +4. `cross-platform-service-usage.md` — the cross-platform usage rules (which combinations are supported, which are anti-patterns). + +Each anchor MUST follow the curated-reference structure from CLAUDE.md (frontmatter with `title`, `product_family`, `capabilities`, `doc_type`, `status`; sections in order: Title, Description, Scope, Content, Prerequisites, Common variants, Related references, Source). + +`product_family` for these anchors is `cross-platform` (path mismatch will fail CI in Phase 3 — get it right now). + +### Generated branch stubs (PLAN.md § Phase 1 step 1) + +Create empty `top-15.json` placeholders or `.gitkeep` directories under `references/generated/{protect,verify,credentials,sso,iga}/`. **Do not populate with real content** — Phase 2 builds shortlists from the docs catalog. Goal in Phase 1 is just directory shape. + +### SKILL.md edits + +- Replace placeholder anchor paths in the routing table with the four real filenames above. +- Confirm the "Multi-skill use cases" table matches `rules/routing-rules.md`. +- Confirm body still fits within 120 lines. + +### Eval prompt set (PLAN.md § Phase 1 step 4) + +Expand `evals/prompts/ping-universal-services.yaml` to: +- ≥10 trigger prompts (cover all 6 services × MT/ST/cross-platform variants) +- ≥5 non-trigger prompts (cleanly belong to `ping-foundation` / `ping-orchestration` / `ping-app-integration` / `ping-quickstart`) +- ≥3 ambiguous prompts (require clarification — e.g., "I want better security on logins" could be Protect or just MFA) + +Run `python -m evals.harness.validate_prompts` after editing. + +### Cross-link + +- Add or update a row in `ping-quickstart/SKILL.md` routing table pointing at universal-services for Protect/Verify/Credentials/IGA intents. +- Add an entry under `plugins/ping-identity/plugin-map.md`. +- Add an entry under `plugins/ping-identity/references/index.json`. + +### Exit gate + +- 4 curated anchors committed, all passing frontmatter validation. +- SKILL.md routing table self-consistent; ≤120 lines. +- Prompt YAML validates against the schema; counts ≥10/≥5/≥3. +- `run_eval --adapter mock --layer 1` still exits 0. + +--- + +## Sub-phase 1.2 — `ping-app-integration` content (Day 2, ~1 day) + +**Goal:** same shape as 1.1, with mobile + web + on-prem variants. + +### Curated anchors (PLAN.md § Phase 1 step 2) + +Author all four under `plugins/ping-identity/skills/ping-app-integration/references/curated/`: + +1. `app-integration-overview.md` — when to reach for this skill (implementation-side work) vs. `ping-foundation` (admin-side); the SDK landscape (Android, iOS, React, web, on-prem agents). +2. `mobile-integration-basics.md` — Android + iOS SDK orientation; common auth flows (OIDC, MFA, biometric); known pitfalls (token storage, deep linking, refresh). +3. `web-integration-basics.md` — React SDK + browser auth flows; PKCE; session handling; OIDC vs. SAML decision rule. +4. `integration-troubleshooting-basics.md` — top failure modes (CORS, redirect URI mismatch, token introspection, clock skew) and their fixes. + +`product_family` should be `cross-platform` for the overview and troubleshooting anchors; mobile/web anchors can stay `cross-platform` since they cover SDKs that target multiple Ping platforms. + +### Generated branch stubs + +Create directories under `references/generated/{mobile,web,orchestration-sdks,on-prem-integration}/` (empty placeholders only). + +### SKILL.md edits + +Verify routing table matches the 4 anchors; verify ≤120 lines. + +### Eval prompt set + +Expand `evals/prompts/ping-app-integration.yaml` to ≥10 / ≥5 / ≥3. + +Trigger prompts must cover: +- Mobile (Android, iOS) — at least 3 +- Web / React — at least 3 +- On-prem app integration — at least 2 +- Orchestration SDK (DaVinci-from-app) — at least 2 + +Non-trigger prompts must include at least one each of: a `ping-foundation` admin task, a `ping-orchestration` flow-design task, a `ping-quickstart` "where do I start?" prompt. + +### Cross-link + +- `ping-quickstart/SKILL.md` routing table. +- `plugins/ping-identity/plugin-map.md`. +- `plugins/ping-identity/references/index.json`. + +### Exit gate + +Same shape as 1.1. + +--- + +## Sub-phase 1.3 — `ping-identity-for-ai` content (Day 3 morning, ~½ day) + +**Goal:** the smallest of the three skills (4 anchors, but the domain is newer and content depth will be lighter). + +### Curated anchors (PLAN.md § Phase 1 step 3) + +Author all four under `plugins/ping-identity/skills/ping-identity-for-ai/references/curated/`: + +1. `identity-for-ai-overview.md` — what "identity for AI" means at Ping; the three buckets (verified trust, agent security, AI app auth); how this skill differs from the other five. +2. `verified-trust-overview.md` — the Verified Trust product: capabilities, where it plugs in (DaVinci flow, app-side, policy). +3. `agent-security-patterns.md` — patterns for securing autonomous / semi-autonomous agents calling Ping APIs; auth model; token scoping; revocation. +4. `workforce-helpdesk-ai.md` — workforce helpdesk AI use case (employee asks AI agent → agent calls Ping APIs on their behalf); identity verification pattern; audit pattern. + +Note: PLAN.md does NOT list a `references/generated/` branch list for this skill. Skip generated branches in Phase 1; revisit in Phase 2 when the docs catalog is scanned. + +### SKILL.md edits + +Verify routing table; ≤120 lines. + +### Eval prompt set + +Expand `evals/prompts/ping-identity-for-ai.yaml` to ≥10 / ≥5 / ≥3. Trigger prompts must include: agent calling Ping APIs, verifiable trust for AI output, helpdesk-AI auth, AI app auth (LLM-fronted apps), and the negative case (a regular customer-identity flow that happens to use ML — should NOT activate this skill). + +### Cross-link + +- `ping-quickstart/SKILL.md` (very important — this is the newest concept and quickstart needs to know how to point at it). +- `plugin-map.md` and `references/index.json`. + +### Exit gate + +Same shape as 1.1. + +--- + +## Sub-phase 1.4 — Layer 1 eval iteration (Day 3 afternoon – Day 4 morning, ~1 day) + +**Goal:** pass bar — 90% trigger / 90% non-trigger / 80% ambiguous-handled — across all 6 skills, on the `claude` adapter. + +This is the loop where most rework happens. Budget for 2–3 iterations. + +### Steps + +1. Run `python -m evals.harness.run_eval --adapter claude --layer 1 --skill all`. Save output to `evals/results/2026-06-01/claude.json` (use the appropriate dated dir). +2. **Diagnose failures by category:** + - **Wrong umbrella selected** — usually a SKILL.md "When to use" wording problem. Tighten the disambiguation language; add the failing prompt's intent class to "When NOT to use". + - **Ambiguous prompt routed without clarification** — strengthen the "what are you trying to do?" decision tree. + - **Two skills both activated unnecessarily** — `rules/routing-rules.md` precedence is unclear; tighten. +3. Edit only the `SKILL.md` files and `rules/routing-rules.md` — do NOT edit the prompt YAMLs to make the test pass (that defeats the eval). +4. Re-run; iterate until pass bar is hit for all 6 skills. +5. Save final results in `evals/results//claude.json` (gitignored is fine; this is not a CI gate yet — Phase 3 turns it into one). + +### What "passing" means here + +Per PLAN.md § Layer 1: ≥90% of trigger prompts activated the right skill, ≥90% of non-trigger prompts correctly NOT activated, ≥80% of ambiguous prompts got a clarifying question (not a guess). + +### Risk handling + +If a single skill is stuck below the bar after 3 iterations, **don't lower the bar**. Two options: +- (a) Move the failing prompts to the ambiguous bucket if they genuinely belong there. +- (b) Escalate as a documented "known limitation" to be fixed in Phase 2 with a curated-anchor rewrite. + +Option (b) requires an entry in `risks` section of the PR description. + +### Exit gate + +- All 6 skills meet the Layer 1 pass bar on the `claude` adapter. +- `evals/results//claude.json` is committed (or referenced in PR description). + +--- + +## Sub-phase 1.5 — End-to-end composition test + PR prep (Day 4 afternoon, ~½ day) + +**Goal:** verify PLAN.md's Phase 1 Exit Criterion — the cross-skill routing case actually works. + +### The canonical test + +> "I want to add KYC during registration on my React app" + +This prompt must: +1. Activate `ping-quickstart` first (free-text intent, platform unknown). +2. After clarification, route to `ping-orchestration` (registration flow) + `ping-universal-services` (KYC = Verify) + `ping-app-integration` (React). +3. Load curated anchors from each: at least one anchor per skill. Concretely: + - `ping-orchestration/references/curated/.../` — a registration flow anchor + - `ping-universal-services/references/curated/choosing-the-right-service.md` (Verify branch) and likely `service-invocation-patterns.md` + - `ping-app-integration/references/curated/web-integration-basics.md` +4. Produce a plan that names: which Ping product (PingOne MT or ST), which Verify policy, which React SDK calls, and the sequence. + +### Steps + +1. Run the canonical prompt manually (or scripted) against the `claude` adapter. +2. Capture the full transcript (skill activations + Read tool calls + final plan). +3. Verify each of the 4 conditions above. +4. If any condition fails, return to 1.1–1.4 to fix the underlying skill or rule. Do **not** ship Phase 1 with a broken composition case. +5. Author 2 additional composition prompts to spot-check the pattern works generally: + - "Add MFA + risk-based step-up on iOS" → orchestration + universal-services + app-integration + - "Build an AI agent that can reset employee passwords" → identity-for-ai + foundation +6. Add the three composition prompts to `evals/prompts/composition.yaml` (a NEW file — orienting a future Phase 2/3 to test composition explicitly). Validate against the schema. + +### PR + +Open a single PR titled `feat: Phase 1 — author 3 planned umbrella skills`. + +PR description checklist: +- [ ] All 12 curated anchors authored and frontmatter-valid (4 + 4 + 4) +- [ ] All 3 SKILL.md files ≤120 lines +- [ ] All 3 prompt YAMLs at ≥10/≥5/≥3 +- [ ] Layer 1 eval `evals/results//claude.json` shows pass bar for all 6 skills +- [ ] Canonical KYC composition test transcript attached +- [ ] `ping-quickstart/SKILL.md` cross-links updated +- [ ] `plugin-map.md` + `references/index.json` updated +- [ ] `rules/routing-rules.md` updated if precedence changed during 1.4 iteration +- [ ] No internal-only references introduced (Glean, Polaris, internal Slack) +- [ ] Each new curated anchor has `Related references` filled in (cross-skill linkage) + +### Exit gate (Phase 1 complete) + +PLAN.md exit criterion verbatim: + +> End-to-end test passes — free-text intent ("KYC during registration on my React app") routes correctly through `ping-quickstart` → `ping-orchestration` + `ping-universal-services` + `ping-app-integration`, returns a curated plan citing the right anchors. + +Plus the PR-checklist items above. + +--- + +## Deliverables summary (final inventory at Phase 1 close) + +### New files (12 curated anchors + 1 composition prompt set) + +``` +plugins/ping-identity/skills/ping-universal-services/references/curated/ + universal-services-overview.md + choosing-the-right-service.md + service-invocation-patterns.md + cross-platform-service-usage.md + +plugins/ping-identity/skills/ping-app-integration/references/curated/ + app-integration-overview.md + mobile-integration-basics.md + web-integration-basics.md + integration-troubleshooting-basics.md + +plugins/ping-identity/skills/ping-identity-for-ai/references/curated/ + identity-for-ai-overview.md + verified-trust-overview.md + agent-security-patterns.md + workforce-helpdesk-ai.md + +evals/prompts/composition.yaml +``` + +### Edited files + +- All 3 planned-skill `SKILL.md` files (routing tables finalized) +- `ping-quickstart/SKILL.md` (cross-links) +- `rules/routing-rules.md` (any disambiguation tightening from 1.4) +- `plugins/ping-identity/plugin-map.md` +- `plugins/ping-identity/references/index.json` +- `evals/prompts/ping-universal-services.yaml` (expand to spec) +- `evals/prompts/ping-app-integration.yaml` (expand to spec) +- `evals/prompts/ping-identity-for-ai.yaml` (expand to spec) + +### Generated branch directories (empty placeholders only — populated in Phase 2) + +``` +ping-universal-services/references/generated/{protect,verify,credentials,sso,iga}/ +ping-app-integration/references/generated/{mobile,web,orchestration-sdks,on-prem-integration}/ +``` + +--- + +## Day-by-day timeline (4-day window, May 31 – June 3 per PLAN.md) + +| Day | Sub-phase | Focus | Hours | +|---|---|---|---| +| Day 1 AM | 1.0 | Outlines, route-rule check, adapter smoke | 2–3 h | +| Day 1 PM | 1.1 | `ping-universal-services` anchors + prompts + cross-links | 5–6 h | +| Day 2 | 1.2 | `ping-app-integration` anchors + prompts + cross-links | 6–8 h | +| Day 3 AM | 1.3 | `ping-identity-for-ai` anchors + prompts + cross-links | 4–5 h | +| Day 3 PM | 1.4 | Layer 1 eval — first run + first iteration | 3–4 h | +| Day 4 AM | 1.4 cont. | Layer 1 eval — second iteration if needed | 3–4 h | +| Day 4 PM | 1.5 | Composition test + PR prep | 3–4 h | + +Total: ~28–34 hours of focused authoring + eval work. + +--- + +## Risks specific to Phase 1 + +| Risk | Likelihood | Mitigation | +|---|---|---| +| Curated content drifts beyond orientation depth and burns time | High | Hard cap each anchor at 200–400 lines per CLAUDE.md; if a topic needs more, split into a Phase 2 follow-up anchor. | +| Layer 1 eval doesn't converge — two skills keep tying | Medium | `rules/routing-rules.md` precedence ladder is the disambiguation tool; add a tie-breaker clause rather than rewriting SKILL.md repeatedly. | +| `ping-identity-for-ai` content is hardest to source (newest product area) | Medium | If Verified Trust / agent-security docs are thin, anchor body cites the public Ping AI announcements + names the product, defers detail to a `references/runtime/docs-mcp-routing.md` pointer. Document gap in PR description. | +| Claude API rate limits during 1.4 eval iteration | Low | Budget at most 3 full Layer 1 sweeps × 6 skills × ~18 prompts ≈ 324 API calls; well within standard limits. | +| Composition test reveals a structural gap that requires SKILL.md surgery on a live skill (e.g., `ping-orchestration`) | Medium | Allowed — fix in this PR. Re-run Layer 1 for the changed skill. | +| Frontmatter `product_family` mismatch caught only at Phase 3 CI | Low | Validate locally via `python -m evals.harness.validate_prompts` and a manual frontmatter grep before PR open. | + +--- + +## Out of scope for Phase 1 (deferred to Phase 2) + +- Populating `references/generated//top-N.json` files. Phase 1 only creates the directories. +- Layer 2 anchor-selection eval. Phase 1 only requires Layer 1 to pass; Layer 2 becomes a gate in Phase 2. +- Layer 3 plan-quality eval. Weekly cadence starts in Phase 2. +- Upgrading curated anchors beyond orientation depth — that's explicitly Phase 2 work for "weak anchors flagged by Phase 1 evals (Layer 3 score <4.0)." +- The `ping-identity-for-ai` generated-branch list (PLAN.md § Phase 1 does not require it). +- CI gating. Eval is local-only in Phase 1; CI integration is Phase 3. + +--- + +## Open questions to resolve before starting + +These map to PLAN.md § "Decisions needed from product": + +1. **Decision #4 (Docs MCP availability)** — Phase 1 assumes `references/runtime/docs-mcp-routing.md` documents the pattern with public docs as fallback. Confirm before authoring the runtime stubs in 1.1–1.3. +2. **Decision #7 (LLM API budget)** — Layer 1 in Phase 1 only needs Claude. Codex + Gemini are Phase 3+. No blocker for Phase 1 if Claude API access is available. +3. **`ping-identity-for-ai` content depth** — confirm with product whether Verified Trust and agent-security material is shareable at orientation depth on a public repo (Phase 4 strips internal references; better to author public-safe from the start). diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..f8e0139 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,528 @@ +# Delivery Plan — Agent Skills v1 (6 Umbrella Skills, Cloudflare-Inspired) + +> Companion to [SUMMARY.md](SUMMARY.md). v1 ships the 6 umbrella skills defined in the [Ping Identity Agent Skill Strategy doc](https://docs.google.com/document/d/1ts57b476DNIEduopqq5fSwyJx5RZSWL72UVGj_EVRYk) § 4 "In Practice". Layout is **Cloudflare-inspired** — `commands/`, `rules/`, `skills/`, `.claude-plugin/`, `.cursor-plugin/` — extended for Ping with a 3-tier reference model (`curated/` + `generated/` + `runtime/`) and a lightweight eval harness. Helix is the production runtime; MCPs are out of scope for v1. + +--- + +## North star + +A user types something like _"I want to add MFA to my mobile banking app"_ and the skill set: + +1. **Orients** — `ping-quickstart` detects the platform and routes to the right umbrella skill. +2. **Plans** — the umbrella skill (typically `ping-foundation` + `ping-orchestration` + `ping-app-integration`) loads the relevant curated anchor and produces a concrete configure-and-build plan. +3. **(Stretch) Executes** — Helix conversation APIs consume the plan and apply it to a tenant via product-specific Helix tools, behind policy/approval gates. + +--- + +## Skill set for v1 — 6 umbrella skills + +This is the **canonical set from the strategy doc § 4**. Three are already live in the repo (`ping-quickstart`, `ping-foundation`, `ping-orchestration`); three are planned (`ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`). + +| Skill | Status | Scope (per strategy doc § 4) | +|---|---|---| +| `ping-quickstart` | Live | Front door. Product-family selection, cloud vs. on-prem routing, workforce vs. customer identity orientation, common starting paths, key prerequisites, and links into the other five umbrella skills. | +| `ping-foundation` | Live | Tenant and environment setup, app setup, directories, policy, branding, admin basics across PingOne MT, PingOne ST (AIC), PingFederate, PingAccess, PingDirectory, PingID. | +| `ping-orchestration` | Live | DaVinci flows, AIC and PingAM journey patterns, flow composition, decisioning patterns, handoff to app integration, troubleshooting for orchestration paths. | +| `ping-universal-services` | **Live (v1)** | Protect, Verify, Credentials, and other shared service references; cross-product service usage; service selection guidance. | +| `ping-app-integration` | **Live (v1)** | Android, iOS, React SDK references; web app integration; mobile and browser auth flows; orchestration SDK references; on-prem app-side integration when the primary task is implementation. | +| `ping-identity-for-ai` | **Live (v1)** | Identity for AI solution references, Verified Trust references, workforce helpdesk AI use cases, agent security patterns, AI app auth patterns. | + +### Decision rule for adding a 7th skill (from strategy doc) + +A new skill is created only if it has **all four** of: +- A distinct trigger +- A distinct user outcome +- Enough depth that routing through references materially improves results +- Low overlap with an existing umbrella + +Otherwise, add it as a reference pack inside an existing umbrella, not a new skill. + +### Out of scope for v1 + +- On-prem-specialist umbrella (PingFederate, PingAccess, PingDirectory get tagged sub-routes inside `ping-foundation`, not their own skill). +- IGA-specific umbrella (lives as a service inside `ping-universal-services`). +- Full SDK plugin (`plugins/ping-identity-sdks/` from strategy doc § 2.3) — defer to v1.1. +- Operational use-case plugin (`plugins/ping-identity-ops/`) — defer to v1.1. +- MCP-based execution path — re-evaluate post-v1 when AIC/DaVinci MCP servers stabilize. + +--- + +## Routing taxonomy (consistent across every skill) + +Every `SKILL.md` routes in this order, per strategy doc § 4: + +1. **What are they trying to do?** — setup, configure, orchestrate, integrate, secure, troubleshoot. +2. **Which platform family applies?** + - PingOne MT (multi-tenant) + - PingOne ST / AIC (single-tenant) + - Ping Software Suite (PingFederate, PingAccess, PingDirectory, PingID, PingAM) + - Cross-platform / Universal Services +3. **Which exact product or service applies?** +4. **Which reference tier should be used?** — curated anchor → generated shortlist → live Docs MCP. + +--- + +## Helix runtime + +Helix is the production execution backbone but is **NOT a skill** in v1. Per the strategy doc, "MCP-based execution" is deferred and Helix is documented inside each skill's `references/runtime/` tier as the production-bound path. The decision rule for sandbox-vs-production runtime lives in `rules/runtime-selection.md` and is referenced from each skill's `references/runtime/docs-mcp-routing.md`. + +When/if Helix execution becomes a first-class umbrella skill (v1.1+), it will pass the four-criteria decision rule above. For v1 it stays as a **runtime tier**, not a skill. + +--- + +## Repo layout (Cloudflare-inspired, Ping-extended) + +This layout follows Cloudflare's structure for the public-facing surface (`.claude-plugin/`, `.cursor-plugin/`, `commands/`, `rules/`, `skills/`) and extends it with the Ping-specific reference tier model and eval harness. See [strategy doc § 5 "Recommended repo structure"](https://docs.google.com/document/d/1ts57b476DNIEduopqq5fSwyJx5RZSWL72UVGj_EVRYk). + +``` +.claude-plugin/ + plugin.json # Claude Code plugin manifest + marketplace.json # Claude Code marketplace listing +.cursor-plugin/ + marketplace.json # Cursor marketplace listing +.well-known/ + agent-skills/ + index.json # Cloudflare Agent Skills Discovery RFC v0.2.0 (Ping addition) +.github/ + workflows/ # CI: validate-skills, build-reference-manifests, sync-doc-metadata, run-evals +commands/ # Cloudflare-style slash commands (stretch) + configure-mfa.md + scaffold-app-integration.md + build-davinci-flow.md +rules/ # Top-level rules referenced by all skills + authoring-rules.md # Frontmatter contract, body length, naming + routing-rules.md # Skill selection precedence + runtime-selection.md # Sandbox-vs-production decision rule + workers.mdc # Cursor-style .mdc rule (Cloudflare parity) +shared/ # Ping-specific cross-skill assets (NOT in Cloudflare repo) + taxonomies/ + platform-families.md + capability-map.md + service-map.md + routing-rules.md + schemas/ + doc-frontmatter-schema.json + reference-manifest-schema.json + templates/ + SKILL.template.md + curated-reference.template.md + generated/ + global-doc-catalog.json + docs-by-platform.json +plugins/ + ping-identity/ + .claude-plugin/plugin.json + skills/ # The 6 umbrella skills + ping-quickstart/ + ping-foundation/ + ping-orchestration/ + ping-universal-services/ + ping-app-integration/ + ping-identity-for-ai/ +evals/ # Ping addition — eval harness (see § Evaluation) + prompts/ + golden/ + schemas/ + scorecards/ + harness/ + results/ +ping-marketplace.json # Ping Marketplace metadata for the entire repo +CONTRIBUTING.md +README.md +LICENSE +``` + +### Per-skill internal structure + +Every skill follows this exact shape (per strategy doc § 6 "Internal structure for each skill"): + +``` +plugins/ping-identity/skills// + SKILL.md # ≤120 lines; routing decision tree only + ping-marketplace.json # Marketplace metadata (icons, tags, filters) + references/ + curated/ # Hand-authored canonical docs (5–10 files) + generated/ # Capped, ranked shortlists per branch (20–50 max) + runtime/ # Pointers/rules for Docs MCP retrieval (no copied content) +``` + +### Reference tier semantics (per strategy doc § 6) + +| Tier | Purpose | Size cap | Source of truth | Update mode | +|---|---|---|---|---| +| `curated/` | Hand-picked canonical playbooks; the agent loads 1–3 per task | 5–10 docs | This repo | Manual | +| `generated/` | Capped top-N shortlist per platform/product branch | 20–50 per branch | Generated from docs frontmatter | CI on docs publish | +| `runtime/` | Routing rules and pointers for live Docs MCP retrieval | No copied content | Docs MCP server | Live | + +--- + +## Phase 0 — Restructure (May 30) ✅ COMPLETE + +**Goal:** Repo skeleton matches the Cloudflare-inspired layout, the 6 umbrella skills exist as scaffolds, the three live skills are reorganized into the canonical reference tier shape, and the eval harness is runnable. + +### Steps + +1. Create `commands/`, `rules/`, `evals/` top-level directories. Add `.well-known/agent-skills/index.json` stub. Add Cursor `.mdc` rule under `rules/` for Cloudflare parity. +2. Verify `.claude-plugin/marketplace.json` and `.cursor-plugin/marketplace.json` both exist and point at `plugins/ping-identity/`. Author `.claude-plugin/plugin.json` and `.cursor-plugin/marketplace.json` if missing. +3. Reorganize the three live skills (`ping-quickstart`, `ping-foundation`, `ping-orchestration`) so each has the full `references/{curated,generated,runtime}/` tier set per strategy § 6. Existing curated content is already in `references/curated/`; add `references/runtime/docs-mcp-routing.md` stubs for each. +4. Scaffold the three planned skills (`ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`) with empty SKILL.md (≤120 lines, agentskills.io frontmatter), `ping-marketplace.json`, and the `references/{curated,generated,runtime}/` directory tree. +5. Author `rules/authoring-rules.md` (port from `shared/templates/AUTHORING-RULES.md`), `rules/routing-rules.md` (port from `shared/taxonomies/routing-rules.md`), and `rules/runtime-selection.md` (NEW — sandbox-vs-production decision rule). +6. Stand up the eval harness: prompt-set JSON Schema, validator, Layer 1 + Layer 2 runner, mock adapter, pytest coverage, all six skills with prompt sets at the agreed minimums. + +**Exit criterion:** Six umbrella skill directories exist with the full per-skill tree; `python -m evals.harness.validate_prompts` and `python -m evals.harness.run_eval --adapter mock --layer 1` both exit 0; both `.claude-plugin/` and `.cursor-plugin/` manifests resolve. + +> **Status (2026-06-01):** Exit criterion met. All 6 skill scaffolds exist; mock eval exits 0; both plugin manifests present. + +--- + +## Phase 1 — Author the three planned umbrella skills (May 31 – June 1) ✅ COMPLETE + +**Goal:** `ping-universal-services`, `ping-app-integration`, and `ping-identity-for-ai` ship at orientation depth — each with a working SKILL.md decision tree and ≥3 curated anchors. + +### Steps + +1. **`ping-universal-services`** — curated anchors per strategy doc § 7: + - `universal-services-overview.md` + - `choosing-the-right-service.md` + - `service-invocation-patterns.md` + - `cross-platform-service-usage.md` + - Generated branches: `protect/`, `verify/`, `credentials/`, `sso/`, `iga/` (top-15 stubs each). +2. **`ping-app-integration`** — curated anchors per strategy doc § 7: + - `app-integration-overview.md` + - `mobile-integration-basics.md` + - `web-integration-basics.md` + - `integration-troubleshooting-basics.md` + - Generated branches: `mobile/`, `web/`, `orchestration-sdks/`, `on-prem-integration/` (top-20 stubs each). +3. **`ping-identity-for-ai`** — curated anchors per strategy doc § 7: + - `identity-for-ai-overview.md` + - `verified-trust-overview.md` + - `agent-security-patterns.md` + - `workforce-helpdesk-ai.md` +4. Each skill ships ≥10 trigger / ≥5 non-trigger / ≥3 ambiguous prompts in `evals/prompts/.yaml`. +5. Cross-link each new skill from `ping-quickstart/SKILL.md` and from `rules/routing-rules.md`. +6. Run Layer 1 eval against the Claude adapter; iterate until all 6 skills pass (90% trigger / 90% non-trigger / 80% ambiguous). + +**Exit criterion:** End-to-end test passes — free-text intent ("KYC during registration on my React app") routes correctly through `ping-quickstart` → `ping-orchestration` + `ping-universal-services` + `ping-app-integration`, returns a curated plan citing the right anchors. + +> **Status (2026-06-01):** Exit criterion met. All 12 curated anchors authored (4 per new skill). All 6 skills pass Layer 1 eval at ≥90/100/80% on the Claude Bedrock adapter (`evals/results/2026-06-01/claude.layer1.json`). Also completed: full repo audit (119 issues found and fixed), `ClaudeAdapter` implemented via Bedrock, `validate_prompts` and `run_eval` composition-file handling fixed. The `ping-foundation` T-07 and `ping-quickstart` T-09 prompts sit exactly at the 90% floor — intentional known-good edge cases documented in eval results. + +**Additional work completed beyond Phase 1 plan:** +- Full verify/review/optimize pass across all 6 skills (119 issues fixed in 43 files): frontmatter gaps, routing placeholders, line-count violations, UI navigation language, missing Scope sections, broken index.json paths +- `ClaudeAdapter` implemented using Bedrock (`eu.anthropic.claude-sonnet-4-6`); auto-detects `CLAUDE_CODE_USE_BEDROCK` env flag +- `evals/prompts/composition.yaml` created (5 cross-skill routing test cases) +- `evals/harness/validate_prompts.py` and `run_eval.py` updated to skip composition file correctly + +--- + +## Phase 2 — Curated content depth + reference manifests (June 2 – June 3) ✅ COMPLETE + +**Goal:** Generated shortlists land in every skill branch; curated anchors are upgraded from orientation depth to task-completing depth where Phase 1 left gaps. + +### Steps + +1. Author `shared/schemas/reference-manifest-schema.json` (already present — verify it covers the v1 generated tier). +2. Build `.github/workflows/build-reference-manifests.yml` — scans frontmatter, generates `top-N.json` per branch. +3. Run the manifest builder against the existing docs catalog; commit the generated `top-N.json` files to each skill's `references/generated//` subdir. +4. Upgrade weak curated anchors flagged by Phase 1 evals (any anchor scoring <4.0 on Layer 3 plan-quality). +5. Each skill: ≥3 trigger prompts and ≥2 non-trigger prompts in `evals/prompts/`. Layer 1 + Layer 2 must pass for all 6 skills. + +**Exit criterion:** All 6 skills pass Layer 1 (≥90/90/80%) and Layer 2 (≥85% per-prompt anchor selection); generated shortlists exist for every documented branch; routing eval against a 25-prompt benchmark scores ≥80/100. + +> **Status (2026-06-03):** Exit criterion met. All 6 skills pass Layer 1 (100/100/100 except ping-quickstart at 90%). Generated shortlists built for all skill branches (14 manifests across 6 skills). Builder script at `scripts/build_reference_manifests.py`; CI workflow at `.github/workflows/build-reference-manifests.yml`. All curated anchors upgraded to task-completing depth during Phase 1 continuation work. + +--- + +## Phase 3 — Repo + CI hardening (June 4) ⏳ NEXT + +### Steps + +1. `CONTRIBUTING.md`: authoring workflow, frontmatter contract, eval requirement, PR checklist. +2. CI workflows: + - `.github/workflows/validate-skills.yml` — frontmatter schema, broken-link check, SKILL.md ≤120 lines, name-matches-directory + - `.github/workflows/run-evals.yml` — Layer 1 + Layer 2 on every PR (blocks merge below pass bar) + - `.github/workflows/build-reference-manifests.yml` — already in Phase 2 +3. Multi-IDE manifests verified: `.claude-plugin/`, `.cursor-plugin/`, `.well-known/agent-skills/index.json`. README install table for OpenCode / Codex / Gemini CLI. +4. `npx skills-ref validate` scaffold (stub script) referenced from CONTRIBUTING.md. + +**Exit criterion:** A clean PR fails CI when frontmatter is malformed, a routing table points to a missing file, a SKILL.md exceeds 120 lines, or eval scores drop below pass bar. + +--- + +## Phase 4 — Public launch + +### Steps + +1. Strip all internal-only references: Glean MCP, Polaris, internal Slack pointers, internal tenant URLs. +2. Repo rename: `brando-dill_pingcorp/agent-skills` → `pingidentity/agent-skills`. +3. Flip visibility public; verify `gh repo view` is clean. +4. Smoke test from a fresh machine: `/plugin marketplace add pingidentity/agent-skills`, ask 5 sample router questions, confirm correct routing. + +**Exit criterion:** Public repo installable in <2 minutes from any supported IDE. + +--- + +## Phase 5 — Distribution + +### Steps + +1. Submit `ping-marketplace.json` for each skill to Ping Marketplace. +2. Publish to AI Marketplaces: Claude Code, Cursor, OpenCode (`npx skills add`), Codex, Gemini CLI. +3. Update Build with AI Docs: add "Agent Skills" section linking to the public repo. +4. Publish blog post: strategy + technical walkthrough + install instructions + 1 demo (free-text → quickstart → product skill → curated plan). + +**Exit criterion:** A developer reading the blog can install + ask a question + get to a curated anchor in <2 minutes. + +--- + +## Phase S (Stretch) — Executable build commands + +Only attempt if Phases 0–3 land on time. + +### Steps + +1. Author `commands/configure-mfa.md`, `commands/scaffold-app-integration.md`, `commands/build-davinci-flow.md` per Cloudflare's command file structure (frontmatter `description`, `argument-hint`, `allowed-tools`). +2. Each command supports `--dry-run` (default) and `--apply`. Dry run prints the plan; apply opens a Helix conversation, posts the plan, polls, and presents the diff for approval before committing. +3. End-to-end test against a sandbox tenant. + +**Exit criterion:** Each command produces a working artifact in a sandbox tenant from a single user prompt. + +--- + +## Orchestration model + +### Skill activation order at runtime + +1. `ping-quickstart` activates on any unstructured user intent where platform is unknown. +2. Quickstart asks ≤3 clarifying questions; resolves to {umbrella skill, platform family, exact product/service}. +3. The umbrella skill loads. +4. Inside the skill, routing logic selects the platform branch and loads 1–3 curated anchors. If insufficient, falls back to generated shortlist; if still insufficient, surgical Docs MCP query. +5. (Stretch) User invokes the matching `/ping:` command which executes the plan via Helix. + +### Cross-skill composition + +Strategy doc § 4 explicitly notes that "complete Ping Identity solutions almost always span more than one skill." Each `SKILL.md` MUST include a "Multi-skill use cases" section showing the typical composition (already present in the live `ping-quickstart` and `ping-foundation` skills — extend pattern to all 6). + +### Routing rule precedence (`rules/routing-rules.md`) + +1. If the user asks "what should I use?" or "where do I start?", route to `ping-quickstart`. +2. If the user names a platform directly ("I'm using AIC"), skip quickstart, route to the most specific umbrella skill for the named task. +3. If the user describes a workflow without naming a product, route to `ping-quickstart`. +4. Cross-skill composition is the norm, not an exception — `SKILL.md` "Multi-skill use cases" sections teach this. + +--- + +## Evaluation + +Per strategy doc § 2.7: "Testing and eval frameworks are a progressive evolution, not a day-1 requirement. We keep the investment low." Phase 0 ships a runnable Layer 1 + Layer 2 harness; Phase 3 makes it a CI gate; Layers 3–5 land progressively. + +Eval is the gate that turns "we shipped skills" into "we shipped skills that work." Every skill must pass Layer 1 + Layer 2 before merge from Phase 3 onward. + +### Five evaluation layers + +#### Layer 1 — Routing accuracy (mandatory, every PR from Phase 3) + +**What:** Does the agent send the right user prompt to the right umbrella skill? + +**How:** +- Each skill ships `evals/prompts/.yaml` with: + - ≥10 trigger prompts (should activate this skill) + - ≥5 non-trigger prompts (should NOT activate this skill — they belong elsewhere) + - ≥3 ambiguous prompts (require clarification before routing) +- Harness: a thin Python script that runs each prompt through Claude / Codex / Gemini in turn and records which skill(s) the agent loaded. +- Score: % of trigger prompts correctly activated, % of non-trigger correctly rejected, % of ambiguous prompts that produced a clarifying question. +- Pass bar: 90% trigger, 90% non-trigger, 80% ambiguous-handled. + +**Why:** Routing failures are the most expensive — a wrong skill produces a wrong plan. + +#### Layer 2 — Curated anchor selection (mandatory, every PR from Phase 3) + +**What:** Within an activated skill, does the agent load the right curated anchor for the specific intent? + +**How:** +- For each curated anchor, define ≥3 prompts that should cause that anchor to be read. +- Harness inspects the agent's tool-call log: did `Read()` happen? +- Pass bar: 85% per-prompt pass (recall ≥ 1.0 AND precision ≥ 0.5 against the prompt's `expected_anchors`). + +**Why:** Loading the wrong anchor wastes tokens and produces irrelevant content. + +#### Layer 3 — Plan quality (LLM-as-judge, weekly) + +**What:** When a skill produces a plan, is it correct, complete, and concrete? + +**How:** +- Define 20 representative prompts per skill, each with a hand-authored "golden plan" (reference answer). +- Run the prompt through the skill set; capture the produced plan. +- LLM-as-judge: ask a DIFFERENT model than the one being evaluated to score on: + - **Correctness** — no factual errors (1–5) + - **Completeness** — covers all required steps (1–5) + - **Concreteness** — names specific products, tools, fields (1–5) + - **Runtime correctness** — picks the right tier (curated / generated / docs MCP) for the prompt's context (1–5) +- Pass bar: average ≥4.0 across all dimensions; no individual prompt scoring <3 on any dimension. + +**Why:** Routing + anchor selection can be right while the actual answer is still vague or wrong. + +#### Layer 4 — Cross-LLM consistency (weekly) + +**What:** Do skills behave acceptably across Claude, Codex, and Gemini? The strategy doc explicitly accepts behavioral drift but we still need a floor. + +**How:** +- Run Layer 1 + Layer 3 evals against ≥3 LLMs. +- Track per-LLM scores in `evals/results//.json`. +- Acceptance: any LLM scoring <70% on Layer 1 triggers a documented "known limitation" entry in README; any LLM scoring <60% blocks v1. + +**Why:** Strategy targets multiple IDEs; we need data, not vibes, on which combinations work. + +#### Layer 5 — End-to-end task completion (Phase S only) + +**What:** When a user invokes `/ping:configure-mfa`, does the resulting tenant actually have a working MFA configuration? + +**How:** Sandbox tenant + scripted assertion; nightly run; pass bar 95% rolling 7-day. + +### Eval directory layout + +``` +evals/ + prompts/ + ping-quickstart.yaml # trigger / non-trigger / ambiguous + ping-foundation.yaml + ping-orchestration.yaml + ping-universal-services.yaml + ping-app-integration.yaml + ping-identity-for-ai.yaml + golden/ + ping-foundation/ + add-mfa-pingone-mt.md # golden plan + ... + schemas/ + prompt-set.schema.json + scorecards/ + routing-eval.md # Layer 1 + anchor-selection-eval.md # Layer 2 + plan-quality-eval.md # Layer 3 + harness/ + run_eval.py # Layer 1 + Layer 2 + judge_plans.py # Layer 3 (Phase 1+) + cross_llm.py # Layer 4 (Phase 1+) + validate_prompts.py # schema validator + adapters/ # mock + claude (+ codex, gemini in Phase 1) + tests/ # pytest covering the harness itself + results/ + 2026-06-01/ + claude.json + gemini.json + codex.json +``` + +### Eval execution schedule + +| Layer | When | +|---|---| +| 1 — Routing | Every PR via CI from Phase 3; blocks merge if .yaml` populated to spec +- ≥3 golden plans in `evals/golden//` +- A passing local Layer 1 + Layer 2 run + +CI rejects skill PRs that lack these artifacts (Phase 3+). + +--- + +## v1.1 backlog (post-June 10) + +Ordered by leverage: + +1. **MCP execution path** — once AIC MCP supports unattended/headless auth and DaVinci's MCP path is decided, add MCP as a sandbox-side runtime alongside Helix. +2. **`plugins/ping-identity-sdks/`** — SDK skills as their own plugin per strategy doc § 2.3. +3. **`plugins/ping-identity-ops/`** — Operational / use-case skills per strategy doc § 2.3. +4. **More build commands** — `/ping:set-up-ciam`, `/ping:add-passwordless`, `/ping:migrate-from-forgerock`. +5. **Docs frontmatter pipeline + CI metadata sync** — automated generated shortlists from docs catalog (built in Phase 2; this is the productionization). +6. **Marketplace sync CI** — auto-publish `ping-marketplace.json` on merge. +7. **Helix-as-a-skill** — re-evaluate when Helix tooling matures and the four-criteria decision rule is met. + +--- + +## External dependencies + +### Runtime + +| Dependency | Used for | Phase | Criticality | +|---|---|---|---| +| **Ping Docs site** (`docs.pingidentity.com`) | Tier-3 retrieval; primary backing for skills | All phases | Hard | +| **Docs MCP server** | Live tier-3 retrieval | Phase 2 onward | Soft (docs site is fallback) | +| **Helix conversation APIs** | Production execution (Phase S only) | Phase S | Soft for v1 | + +### Specifications + +| Spec | Used for | Criticality | +|---|---|---| +| agentskills.io spec | SKILL.md frontmatter | Hard | +| Cloudflare Agent Skills RFC v0.2.0 | `.well-known/agent-skills/index.json` | Hard for Phase 3 | +| Claude Code plugin marketplace format | Distribution | Hard | +| Cursor remote rules format (incl. `.mdc`) | Distribution | Hard | +| OpenCode `npx skills add` | Distribution | Soft | + +### Tooling and CI + +| Tool | Used for | Criticality | +|---|---|---| +| GitHub Actions | CI pipelines + eval harness execution | Hard | +| Python 3.11+ | Eval harness runtime | Hard | +| Anthropic / OpenAI / Google API access | Cross-LLM eval (Layer 4) | Hard from Phase 3 | +| JSON Schema validator (`ajv-cli`) | Frontmatter validation | Hard | +| Markdown link checker (`lychee`) | Broken-link CI gate | Hard | + +### v1 hard list + +Must work before going public on June 8: + +1. agentskills.io frontmatter spec stable +2. Claude Code marketplace accepting submissions +3. Cursor remote rules format confirmed +4. GitHub Actions runner availability +5. `pingidentity` GitHub org admin access + +--- + +## Critical-path risks + +| Risk | Mitigation | +|---|---| +| Three planned skills authored in parallel under tight timeline | Each ships at orientation depth (3 curated anchors); use-case depth lands in v1.1. | +| Generated shortlists require docs frontmatter | Phase 2 scans existing `.md` for partial frontmatter; manual fill for canonical anchors; full automation in v1.1. | +| Eval harness blocks development | Layer 1 + 2 runnable in Phase 0 against mock adapter (no API budget); becomes CI gate only at Phase 3. | +| Cross-LLM scores diverge significantly | Layer 4 produces data, not blockers. README lists known good combinations. | +| Repo rename breaks links | Single `grep` sweep before flipping public. | + +--- + +## Decisions needed from product + +### Pre-Phase 0 + +1. **Scope confirmation** — confirm v1 ships exactly the 6 strategy-doc umbrella skills; defer SDK plugin and ops plugin to v1.1. +2. **Cursor `.mdc` rule** — ship one rule for parity with Cloudflare, or skip until Phase 3? +3. **Quickstart clarification depth** — confirm ≤3 questions max. + +### Pre-Phase 1 + +4. **Docs MCP server availability** — by June 8, will the Docs MCP server be reachable from external developer environments, or do `references/runtime/docs-mcp-routing.md` files document the pattern with the public docs site as fallback? +5. **Reference manifest builder** — Phase 2 ships the CI workflow; pre-Phase 1 confirm where the docs catalog lives and whether docs frontmatter is sufficient for shortlist generation. +6. **Repo rename timing** — June 8 (planned) or earlier. + +### Eval-related + +7. **LLM API budget** — Layer 4 cross-LLM evals require Claude + Codex + Gemini API access. Confirm budget owner. +8. **Sandbox tenant for Layer 5** — confirm dedicated PingOne ST + DaVinci sandbox available for nightly e2e runs (Phase S only). +9. **Eval failure response policy** — when CI eval drops below pass bar, does the PR block, or does the system page an owner? + +### Stretch goal scoping + +10. **Stretch ship/no-ship** — confirm `/ping:configure-mfa`, `/ping:scaffold-app-integration`, `/ping:build-davinci-flow` are stretch, not committed. +11. **Helix coordination** — does June 10 launch align with a Helix milestone, or stand alone? diff --git a/README.md b/README.md new file mode 100644 index 0000000..227d558 --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +# Ping Identity Agent Skills + +A public skill package that teaches AI coding agents (Claude Code, Cursor, Copilot CLI, Gemini CLI) how to reason about the Ping Identity platform — which product to use, how to configure it, and how to integrate it into an application. + +Skills provide **judgment and context** that MCP tools lack. MCP tools handle execution; skills handle the *what* and *why* before execution begins. + +--- + +## Install + +**Claude Code** +```bash +/plugin add pingidentity/agent-skills +``` + +**Cursor** — add to `.cursor/settings.json`: +```json +"remoteRules": ["https://raw.githubusercontent.com/pingidentity/agent-skills/main/rules/routing-rules.md"] +``` + +**Copilot CLI / OpenCode** +```bash +npx skills add pingidentity/agent-skills +``` + +**Gemini CLI** — add the plugin path to your `GEMINI.md` or agent configuration. + +> The repo will move to `pingidentity/agent-skills` at public launch (Phase 4). Current location: `brando-dill_pingcorp/agent-skills`. + +--- + +## The 6 umbrella skills + +| Skill | What it does | Trigger when... | +|---|---|---| +| `ping-quickstart` | Front door — detects platform and routes | Platform unknown, "where do I start", migrating from ForgeRock/Okta | +| `ping-foundation` | Tenant setup, app registration, directories, policy, branding | Configuring or administering a Ping platform | +| `ping-orchestration` | DaVinci flows, AIC/PingAM journeys, scripted nodes | Designing or building an authentication flow | +| `ping-universal-services` | Protect, Verify, Credentials, IGA, Authorize — service config and invocation | Adding a shared service to a flow (risk, KYC, credentials, governance) | +| `ping-app-integration` | Android, iOS, React, OIDC, on-prem SDK integration | Wiring Ping into app code; troubleshooting redirect/CORS/token errors | +| `ping-identity-for-ai` | AI agent identity, Verified Trust, helpdesk AI delegation | Securing an AI agent or LLM-fronted app with Ping | + +Skills compose — a complete solution typically spans 2–3 skills. `ping-quickstart` tells you which combination to load. + +--- + +## How it works + +Every skill follows a 3-tier progressive disclosure model: + +``` +Tier 1 — Metadata (~100 tokens) + skill name + description — loaded at discovery for all skills + +Tier 2 — SKILL.md (<5k tokens) + Routing decision tree: intent → platform → reference tier + Loaded in full when the skill activates + +Tier 3 — References (on demand) + curated/ hand-authored canonical anchors (1–3 loaded per task) + generated/ bounded top-N shortlists per platform branch (Phase 2) + runtime/ pointers for live Docs MCP retrieval +``` + +The agent stops at the first tier that answers the question. It never loads all anchors at once. + +--- + +## Repo layout + +``` +plugins/ping-identity/ + skills/ + ping-quickstart/ SKILL.md + references/{curated,generated,runtime}/ + ping-foundation/ + ping-orchestration/ + ping-universal-services/ + ping-app-integration/ + ping-identity-for-ai/ + plugin-map.md skill index and selection rules + references/index.json all curated anchor paths +rules/ + authoring-rules.md frontmatter contract, body length, naming + routing-rules.md skill selection precedence + runtime-selection.md sandbox-vs-production decision rule +shared/ + taxonomies/ platform families, capability map, service map + schemas/ frontmatter JSON schema + templates/ SKILL.md and curated-reference templates +evals/ + prompts/ trigger / non-trigger / ambiguous prompt sets per skill + harness/ Layer 1 + Layer 2 runner, Claude + mock adapters + results/ dated eval run outputs +``` + +--- + +## Eval status + +Layer 1 routing eval — last run **2026-06-01** against `eu.anthropic.claude-sonnet-4-6` via Bedrock: + +| Skill | Trigger | Non-trigger | Ambiguous | Result | +|---|---|---|---|---| +| ping-quickstart | 90% | 100% | 100% | ✅ PASS | +| ping-foundation | 90% | 100% | 100% | ✅ PASS | +| ping-orchestration | 100% | 100% | 100% | ✅ PASS | +| ping-universal-services | 100% | 100% | 100% | ✅ PASS | +| ping-app-integration | 100% | 100% | 100% | ✅ PASS | +| ping-identity-for-ai | 100% | 100% | 100% | ✅ PASS | + +Pass bar: 90% trigger / 90% non-trigger / 80% ambiguous. Full results: `evals/results/2026-06-01/claude.layer1.json`. + +Run the eval yourself: +```bash +pip install "anthropic[bedrock]" pyyaml jsonschema +python3 -m evals.harness.validate_prompts +python3 -m evals.harness.run_eval --adapter claude --layer 1 +# Without an API key: +python3 -m evals.harness.run_eval --adapter mock --layer 1 +``` + +--- + +## Authoring a skill or reference + +Read `shared/templates/AUTHORING-RULES.md` before writing anything — it is the single source of truth. + +Key constraints enforced in review (and Phase 3 CI): +- Every reference `.md` requires a complete frontmatter block (`title`, `product_family`, `capabilities`, `doc_type`, `status` are mandatory) +- `product_family` must match the file's directory path +- Curated anchors must have a `## Scope` section with explicit Covers/Does NOT cover statements +- No UI navigation steps — write field tables and decision rules instead +- Cross-references use repo-relative paths only +- Plugin files (`plugins//`) must not reference `/shared/` +- `SKILL.md` ≤ 120 lines; every new skill PR must include passing Layer 1 eval results + +--- + +## Delivery status + +| Phase | Description | Status | +|---|---|---| +| 0 | Repo restructure + eval harness scaffold | ✅ Complete | +| 1 | Author 3 new skills + Layer 1 eval passing | ✅ Complete (2026-06-01) | +| 2 | Generated shortlists + reference manifests | ⏳ In progress (June 2–3) | +| 3 | CI hardening + CONTRIBUTING.md | Planned (June 4) | +| 4 | Public launch + repo rename | Planned (TBD) | +| 5 | Marketplace distribution + blog post | Planned (TBD) | + +See [PLAN.md](PLAN.md) for full phase details and exit criteria. + +--- + +## License + +See [LICENSE](LICENSE). diff --git a/commands/.gitkeep b/commands/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/superpowers/plans/2026-05-29-phase-0-restructure.md b/docs/superpowers/plans/2026-05-29-phase-0-restructure.md new file mode 100644 index 0000000..72501d2 --- /dev/null +++ b/docs/superpowers/plans/2026-05-29-phase-0-restructure.md @@ -0,0 +1,2260 @@ +# Phase 0 — Restructure Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Bring the repo into the Cloudflare-inspired layout described in [PLAN.md](../../../PLAN.md) Phase 0 and the [Ping Identity Agent Skill Strategy doc § 4 "In Practice"](https://docs.google.com/document/d/1ts57b476DNIEduopqq5fSwyJx5RZSWL72UVGj_EVRYk). The 6 umbrella skills land in `plugins/ping-identity/skills/` (3 already live, 3 newly scaffolded), each with the `references/{curated,generated,runtime}/` tier. Top-level `commands/`, `rules/`, `evals/` directories ship per the Cloudflare pattern. A runnable Layer 1 + Layer 2 eval harness ships ungated in Phase 0 and becomes a CI gate in Phase 3. + +**Architecture:** The repo follows Cloudflare's outer shape — `.claude-plugin/`, `.cursor-plugin/`, `commands/`, `rules/`, `skills/` (under `plugins/ping-identity/`) — and extends it with three Ping-specific additions: a 3-tier reference model (`curated/` + `generated/` + `runtime/`) on each skill per strategy doc § 6, an `evals/` harness, and `shared/` taxonomies that are out of scope for Cloudflare's repo. The 6 v1 skills are exactly the strategy-doc umbrellas: `ping-quickstart`, `ping-foundation`, `ping-orchestration`, `ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`. Helix is **not** a v1 skill — it lives as a runtime tier inside each skill's `references/runtime/` per strategy doc. + +**Tech Stack:** Markdown, YAML (PyYAML), JSON Schema, Python 3.11+, `pytest` for harness tests. No new runtime infra. + +**Branch:** `skills-refactoring` (already created and checked out). + +**Working directory:** `/Users/george.bafaloukas/Dev/tiger-agent-skills` + +--- + +## Departure from the previous draft + +This plan was rewritten from the prior 9-skill cloud-first version once we confirmed the strategy doc's § 4 set is the source of truth. Key changes: + +- **Skill set:** 6 umbrella skills (matches strategy doc § 4), not 9 cloud-product skills. +- **No `_archive/`:** the three live skills stay in place — they already match strategy doc § 4. Only their `references/` directory is reorganized to add the `runtime/` tier. +- **No top-level `skills/` directory:** skills nest under `plugins/ping-identity/skills/` per strategy doc § 5. That preserves the multi-plugin model (`plugins/ping-identity-sdks/` is v1.1). +- **Helix is not a skill:** it's a runtime tier referenced from each skill's `references/runtime/docs-mcp-routing.md`. v1.1 may promote it. +- **Cursor `.mdc` rule added:** Cloudflare ships one (`rules/workers.mdc`); we ship `rules/ping-identity.mdc` for parity. +- **Eval harness retained as Ping addition:** strategy doc § 2.7 says eval is "a progressive evolution" — Phase 0 ships Layer 1 + Layer 2 ungated; Phase 3 makes it a CI gate. + +--- + +## File Structure + +After Phase 0, the repo looks like this. Items marked **NEW** are created by this plan. Items marked **REORGANIZED** keep their content but get extra tier directories. Items marked **MODIFIED** have small content edits. + +``` +.claude-plugin/ + marketplace.json MODIFIED (small content refresh) + plugin.json NEW (Cloudflare parity) +.cursor-plugin/ + marketplace.json NEW (Cloudflare parity) + plugin.json NEW (Cloudflare parity) +.well-known/ + agent-skills/ + index.json NEW — empty stub for Phase 3 to fill + +commands/ NEW + .gitkeep NEW — Phase S fills this + +rules/ NEW + authoring-rules.md PORTED from shared/templates/AUTHORING-RULES.md + routing-rules.md PORTED from shared/taxonomies/routing-rules.md + runtime-selection.md NEW — sandbox vs production decision rule + ping-identity.mdc NEW — Cursor-style rule for Cloudflare parity + +shared/ (existing) unchanged + +plugins/ + ping-identity/ + .claude-plugin/ + plugin.json NEW (per strategy doc § 5) + skills/ + ping-quickstart/ REORGANIZED — add references/runtime/ + SKILL.md (existing) unchanged in Phase 0 + ping-marketplace.json (existing) unchanged + references/ + curated/ REORGANIZED — existing flat refs move into curated/ + getting-started-overview.md (existing — moved one level deeper) + choose-the-right-ping-platform.md (existing — moved) + common-starting-patterns.md (existing — moved) + generated/ (existing) unchanged + runtime/ + docs-mcp-routing.md NEW (stub) + ping-foundation/ REORGANIZED — add references/runtime/ + SKILL.md (existing) + ping-marketplace.json (existing) + references/ + curated/ (existing) unchanged + generated/ (existing) unchanged + runtime/ + docs-mcp-routing.md NEW (stub) + ping-orchestration/ REORGANIZED — add references/runtime/ + SKILL.md (existing) + ping-marketplace.json (existing) + references/ + curated/ (existing) unchanged + generated/ (existing) unchanged + runtime/ + docs-mcp-routing.md NEW (stub) + ping-universal-services/ NEW + SKILL.md NEW (≤120 lines, scaffold) + ping-marketplace.json NEW + references/{curated,generated,runtime}/ NEW (with .gitkeep + runtime stub) + ping-app-integration/ NEW + SKILL.md NEW + ping-marketplace.json NEW + references/{curated,generated,runtime}/ NEW + ping-identity-for-ai/ NEW + SKILL.md NEW + ping-marketplace.json NEW + references/{curated,generated,runtime}/ NEW + +evals/ NEW + README.md NEW + prompts/ + ping-quickstart.yaml NEW + ping-foundation.yaml NEW + ping-orchestration.yaml NEW + ping-universal-services.yaml NEW + ping-app-integration.yaml NEW + ping-identity-for-ai.yaml NEW + golden/.gitkeep NEW (Phase 1 populates per skill) + schemas/ + prompt-set.schema.json NEW — JSON Schema for prompts/*.yaml + scorecards/ + routing-eval.md PORTED+EXTENDED from shared/evals/routing-eval.md + anchor-selection-eval.md NEW — Layer 2 scorecard + plan-quality-eval.md NEW — Layer 3 scorecard (used Phase 1+) + harness/ + pyproject.toml NEW + requirements.txt NEW + run_eval.py NEW — Layer 1 + Layer 2 runner + judge_plans.py NEW — Layer 3 LLM-as-judge stub + cross_llm.py NEW — Layer 4 multi-LLM driver stub + validate_prompts.py NEW — schema validator + adapters/ + __init__.py NEW + base.py NEW — LLMAdapter abstract base + claude.py NEW — Anthropic SDK adapter (stub) + mock.py NEW — deterministic adapter for harness tests + tests/ + __init__.py NEW + test_validate_prompts.py NEW + test_run_eval.py NEW + test_adapters_mock.py NEW + results/.gitkeep NEW (per-day folders generated at run time) +``` + +**Why this layout:** + +- The 6 skill directories under `plugins/ping-identity/skills/` mirror strategy doc § 5 exactly. +- Each skill's `references/{curated,generated,runtime}/` tier matches strategy doc § 6. +- `_archive/` is intentionally absent — the three live skills already match the strategy and stay in place. +- `evals/harness/` is a self-contained Python project so it runs locally in Phase 0 and from GitHub Actions in Phase 3 with no migration. +- `evals/prompts/.yaml` is one file per skill — easy to diff alongside SKILL.md PRs. +- Adapters are pluggable so Layer 4 (cross-LLM) drops in by adding `codex.py` and `gemini.py` without changing the runner. + +--- + +## Eval design — how skill accuracy is measured + +This section defines the contract that every skill PR (Phase 1 onward) must satisfy. The harness scaffolded in Phase 0 is the implementation of this contract. Strategy doc § 2.7 explicitly notes that "Testing and eval frameworks are a progressive evolution, not a day-1 requirement. We keep the investment low" — the design below honors that by shipping Layers 1–2 runnable in Phase 0 and Layers 3–4 as runnable stubs. + +### What "accuracy" means for a Ping skill + +A Ping umbrella skill is a routing + retrieval + planning artifact. There is no single "correct output" — accuracy decomposes into four observable signals: + +1. **Activation accuracy** — given a prompt, does the agent invoke this umbrella skill iff this skill should handle it? +2. **Anchor selection accuracy** — once the skill is active, does the agent open the smallest sufficient set of curated reference files? +3. **Plan accuracy** — does the answer name the right products, services, fields, and runtimes, with no factual errors? +4. **Tier discipline** — does the answer stop at curated when curated is enough, expand to generated only when needed, fall back to Docs MCP only as a last resort? Strategy doc § 0 ("Agent Path") makes this explicit. + +Layer 1 and Layer 2 of the harness measure (1) and (2) deterministically by inspecting the agent's tool-call log. Layer 3 measures (3) and (4) via LLM-as-judge against a hand-authored golden plan. Layer 4 reruns Layers 1 and 3 across Claude / Codex / Gemini. Layer 5 (Phase S) verifies the plan actually produces a working tenant artifact. + +### Prompt set contract — `evals/prompts/.yaml` + +Every umbrella skill ships a YAML prompt set with this shape (validated by `evals/schemas/prompt-set.schema.json`): + +```yaml +skill: ping-orchestration # required, must match the skill directory name +version: 1 +trigger_prompts: # ≥10 — should activate this skill + - id: T-01 + prompt: "I want to build a registration journey in PingOne ST that collects email and sends an OTP." + expected_anchors: # Layer 2: curated paths the agent should load + - plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md + expected_tier: curated # curated | generated | docs-mcp — Layer 3 tier-discipline check + notes: "Sandbox-tier orientation; one curated anchor is sufficient." +non_trigger_prompts: # ≥5 — should NOT activate this skill + - id: N-01 + prompt: "How do I install PingFederate on RHEL?" + expected_skill: ping-foundation +ambiguous_prompts: # ≥3 — should produce a clarifying question, not a route + - id: A-01 + prompt: "I want to add MFA." + expected_clarification_keywords: ["pingone", "aic", "platform", "mt", "st", "workforce", "ciam"] +``` + +### Layer 1 — Routing accuracy (mandatory, every PR from Phase 3) + +**Question answered:** Did the agent activate the right umbrella skill for this prompt? + +**Algorithm:** +1. For each prompt in `trigger_prompts`, `non_trigger_prompts`, `ambiguous_prompts`, send the prompt to the LLM adapter with the 6 SKILL.md files registered as available skills. +2. Record `loaded_skills`: every skill the agent invoked. +3. Score: + - Trigger prompt → **correct** iff `` is in `loaded_skills`. Multi-skill activation is allowed (and expected per strategy doc); the named skill must be present. + - Non-trigger prompt → **correct** iff the prompt's `` is NOT in `loaded_skills` AND the `expected_skill` IS in `loaded_skills` (when non-null). + - Ambiguous prompt → **correct** iff the agent emitted a clarifying question containing ≥1 keyword from `expected_clarification_keywords` AND did not finalize a route. + +**Pass bar (per skill):** ≥90% trigger correct, ≥90% non-trigger correct, ≥80% ambiguous handled. + +### Layer 2 — Curated anchor selection (mandatory, every PR from Phase 3) + +**Question answered:** Within an activated skill, did the agent open the right curated reference files? + +**Algorithm:** +1. For each `trigger_prompt` whose `expected_anchors` is non-empty, run the same prompt through the adapter. +2. Capture every `Read()` call the agent issues during the run. +3. Score: + - **Precision** = `|loaded ∩ expected| / |loaded|` + - **Recall** = `|loaded ∩ expected| / |expected|` + - **Per-prompt pass** = recall ≥ 1.0 AND precision ≥ 0.5. + +**Pass bar (per skill):** ≥85% of `expected_anchors`-bearing prompts pass. + +**Why precision and recall:** Strategy doc § 0 mandates "use the smallest trusted context first." Loading the wrong anchor wastes tokens; missing the right one produces a vague answer. Both must be measured. + +### Layer 3 — Plan quality (LLM-as-judge, weekly + before release) + +**Question answered:** Is the produced plan correct, complete, concrete, and at the right tier? + +**Algorithm:** +1. For each prompt in `evals/golden//.md`, run the prompt through the adapter and capture the full final assistant message. +2. Pass `(prompt, golden_plan, produced_plan)` to a judge LLM (DIFFERENT model than the one being evaluated). +3. Judge scores 1–5 on each of: + - **Correctness** — no factual errors about Ping products, services, fields + - **Completeness** — covers all required steps in the golden plan + - **Concreteness** — names specific products, fields, env vars; no "configure as appropriate" + - **Tier discipline** — used curated when sufficient; only escalated to generated/docs-mcp when curated didn't cover the task; matches `expected_tier` per `rules/runtime-selection.md` +4. Output: per-prompt scores + overall mean per skill. + +**Pass bar (per skill):** mean ≥ 4.0 across all 4 dimensions; zero prompts scoring < 3 on any dimension. + +### Layer 4 — Cross-LLM consistency (weekly) + +**Question answered:** Do skills work acceptably on Codex and Gemini, not just Claude? + +**Algorithm:** +1. Run Layer 1 + Layer 3 with adapters for Claude, Codex, Gemini in parallel. +2. Write per-LLM JSON to `evals/results//.json`. +3. Acceptance: + - Any LLM scoring < 70% on Layer 1 → adds an entry to `README.md § Known limitations`, does NOT block merge. + - Any LLM scoring < 60% on Layer 1 → blocks v1 launch (Phase 4). + +### Layer 5 — End-to-end (Phase S only, scaffolded but unused in Phase 0) + +Out of scope for this plan beyond directory placeholders. + +### Eval pass-bar summary + +| Layer | What it measures | Per-skill bar | When | +|---|---|---|---| +| 1 — Routing | Correct skill activated | 90% trigger / 90% non-trigger / 80% ambiguous | Every PR from Phase 3 | +| 2 — Anchors | Right curated files loaded | 85% per-prompt pass | Every PR from Phase 3 | +| 3 — Plan quality | Correct, complete, concrete, right tier | mean ≥ 4.0; no dim < 3 | Weekly + pre-release | +| 4 — Cross-LLM | Acceptable on Codex/Gemini | ≥70% Layer 1 (warn); ≥60% (block at launch) | Weekly | +| 5 — E2E | Sandbox tenant actually works | 95% rolling 7-day | Nightly, Phase S only | + +### Phase 0 specifically delivers + +- All 6 prompt-set files. The three live skills (`ping-quickstart`, `ping-foundation`, `ping-orchestration`) ship at full v1 minimums (≥10 / ≥5 / ≥3) since their SKILL.md and curated content already exist. The three new skills ship Phase-0 minimums (3 / 2 / 1); Phase 1 expands them once their bodies land. +- `validate_prompts.py` enforcing the schema, with pytest coverage. +- `run_eval.py` capable of executing Layer 1 and Layer 2 against the `mock` adapter (so the harness itself is testable without API budget). +- `judge_plans.py` and `cross_llm.py` as runnable stubs. +- A documented invocation: `python -m evals.harness.run_eval --adapter mock --layer 1` produces a passing run on the placeholder prompt sets — proves the harness end-to-end before Phase 1 wires real LLM calls. + +--- + +## Tasks + +### Task 1: Create the v1 directory skeleton (commands, rules, evals, .well-known, multi-IDE manifests) + +**Files:** +- Create: `commands/.gitkeep` +- Create: `rules/.gitkeep` +- Create: `evals/` subtree (placeholders only; content lands in Tasks 7–13) +- Create: `.well-known/agent-skills/index.json` (empty stub) +- Create: `.cursor-plugin/marketplace.json` +- Create: `.cursor-plugin/plugin.json` +- Create: `.claude-plugin/plugin.json` +- Create: `plugins/ping-identity/.claude-plugin/plugin.json` + +- [ ] **Step 1: Create top-level directories with .gitkeep markers** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +mkdir -p commands rules +mkdir -p evals/prompts evals/golden evals/schemas evals/scorecards evals/harness/adapters evals/harness/tests evals/results +mkdir -p .well-known/agent-skills .cursor-plugin plugins/ping-identity/.claude-plugin +touch commands/.gitkeep rules/.gitkeep evals/golden/.gitkeep evals/results/.gitkeep +``` + +- [ ] **Step 2: Create the `.well-known/agent-skills/index.json` stub** + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/.well-known/agent-skills/index.json`: + +```json +{ + "$schema": "https://agentskills.io/spec/v0.2.0/index.schema.json", + "version": "0.2.0", + "skills": [] +} +``` + +(Phase 3 populates the `skills` array.) + +- [ ] **Step 3: Create `.cursor-plugin/marketplace.json` and `.cursor-plugin/plugin.json`** + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/.cursor-plugin/marketplace.json`: + +```json +{ + "name": "ping-identity-skills", + "owner": { "name": "Ping Identity" }, + "description": "Agent skills for Ping Identity platforms — six umbrella skills covering platform setup, orchestration, universal services, app integration, AI identity patterns, and platform routing.", + "plugins": [ + { + "name": "ping-identity", + "source": "./plugins/ping-identity" + } + ] +} +``` + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/.cursor-plugin/plugin.json`: + +```json +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Six-umbrella agent skill suite for Ping Identity: PingOne MT, PingOne ST (AIC), Ping Software Suite, DaVinci, universal services, app integration, identity for AI.", + "author": { "name": "Ping Identity" }, + "keywords": ["ping", "pingone", "aic", "davinci", "pingfederate", "identity", "authentication", "ciam", "ai-identity"] +} +``` + +- [ ] **Step 4: Create `.claude-plugin/plugin.json` (Cloudflare parity)** + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/.claude-plugin/plugin.json`: + +```json +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Six-umbrella agent skill suite for Ping Identity platforms.", + "author": { "name": "Ping Identity" } +} +``` + +- [ ] **Step 5: Create the inner plugin manifest required by strategy doc § 5** + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/.claude-plugin/plugin.json`: + +```json +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Core Ping Identity platform skills: ping-quickstart, ping-foundation, ping-orchestration, ping-universal-services, ping-app-integration, ping-identity-for-ai.", + "author": { "name": "Ping Identity" } +} +``` + +- [ ] **Step 6: Validate every JSON file parses** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +for f in .claude-plugin/marketplace.json .claude-plugin/plugin.json .cursor-plugin/marketplace.json .cursor-plugin/plugin.json .well-known/agent-skills/index.json plugins/ping-identity/.claude-plugin/plugin.json; do + python3 -c "import json; json.load(open('$f'))" && echo "OK $f" || echo "FAIL $f" +done +``` + +Expected: every line prints `OK`. + +- [ ] **Step 7: Commit** + +```bash +git add commands/ rules/ evals/ .well-known/ .cursor-plugin/ .claude-plugin/plugin.json plugins/ping-identity/.claude-plugin/ +git commit -m "chore: scaffold v1 directory skeleton (Cloudflare-inspired) + +Add commands/, rules/, evals/ top-level dirs and the multi-IDE +manifests required by strategy doc § 5: .claude-plugin/plugin.json, +.cursor-plugin/marketplace.json + plugin.json, plugins/ping-identity/ +.claude-plugin/plugin.json, and a .well-known/agent-skills/index.json +stub for the discovery RFC. + +Refs: PLAN.md Phase 0 step 1; strategy doc § 5" +``` + +--- + +### Task 2: Reorganize the three live skills into the `references/{curated,generated,runtime}/` shape + +The three live skills (`ping-quickstart`, `ping-foundation`, `ping-orchestration`) keep their SKILL.md and existing content. Only their reference layout changes: + +- `ping-quickstart` currently has flat `references/*.md` — those move into `references/curated/`. +- `ping-foundation` and `ping-orchestration` already have `references/curated/` and `references/generated/`. +- All three need a new `references/runtime/docs-mcp-routing.md` stub. + +**Files:** +- Move: `plugins/ping-identity/skills/ping-quickstart/references/*.md` → `plugins/ping-identity/skills/ping-quickstart/references/curated/` +- Create: `plugins/ping-identity/skills/ping-quickstart/references/runtime/docs-mcp-routing.md` +- Create: `plugins/ping-identity/skills/ping-foundation/references/runtime/docs-mcp-routing.md` +- Create: `plugins/ping-identity/skills/ping-orchestration/references/runtime/docs-mcp-routing.md` +- Modify: `plugins/ping-identity/skills/ping-quickstart/SKILL.md` (update reference paths from `references/X.md` to `references/curated/X.md`) + +- [ ] **Step 1: Reorganize ping-quickstart's flat references into `curated/`** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills/ping-quickstart +mkdir -p references/curated +git mv references/getting-started-overview.md references/curated/getting-started-overview.md +git mv references/choose-the-right-ping-platform.md references/curated/choose-the-right-ping-platform.md +git mv references/common-starting-patterns.md references/curated/common-starting-patterns.md +``` + +- [ ] **Step 2: Update SKILL.md reference paths in ping-quickstart** + +Read `/Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills/ping-quickstart/SKILL.md`, then replace all three flat reference paths to point under `curated/`: + +- `references/getting-started-overview.md` → `references/curated/getting-started-overview.md` +- `references/choose-the-right-ping-platform.md` → `references/curated/choose-the-right-ping-platform.md` +- `references/common-starting-patterns.md` → `references/curated/common-starting-patterns.md` + +- [ ] **Step 3: Create the runtime tier stub for all three live skills** + +For each of `ping-quickstart`, `ping-foundation`, `ping-orchestration`, create `references/runtime/docs-mcp-routing.md`: + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills +for s in ping-quickstart ping-foundation ping-orchestration; do + mkdir -p $s/references/runtime +done +``` + +Write the same content (with the skill name substituted) to each `/references/runtime/docs-mcp-routing.md`: + +```markdown +--- +title: Docs MCP routing — +status: current +last_updated: 2026-05-29 +--- + +# Runtime tier — Docs MCP routing for + +This file describes when and how this skill falls back to live Docs MCP retrieval. It is the third tier in the strategy doc § 0 "Agent Path". + +## When to escalate to Docs MCP + +Use Docs MCP only when: +1. The 1–3 curated anchors loaded from `references/curated/` did not answer the question. +2. The bounded shortlist in `references/generated//top-N.json` did not fill the gap. +3. The user's task requires version-specific, current, or long-tail information (e.g., a recently released feature, a deprecation note, a specific API field). + +If any of these is false, do NOT call Docs MCP. Strategy doc § 0 mandates "use the smallest trusted context first." + +## Surgical query rules + +When Docs MCP is required, query it with: +- The exact platform family (PingOne MT, PingOne ST, Ping Software Suite) +- The exact product or service name +- The exact capability (e.g., "MFA policy", not "authentication") +- A version constraint when applicable + +Retrieve specific sections, not full page dumps. + +## Helix as a runtime path + +Production-bound execution runs through Helix conversation APIs. The decision rule for sandbox-vs-production lives in `rules/runtime-selection.md`. Helix is **not** a v1 skill; it is a runtime tier referenced from this file. + +## Related + +- `rules/runtime-selection.md` +- `references/curated/` — tier 1 +- `references/generated/` — tier 2 +``` + +- [ ] **Step 4: Verify the reorganization** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills +for s in ping-quickstart ping-foundation ping-orchestration; do + for tier in curated generated runtime; do + [ -d $s/references/$tier ] && echo "OK $s/references/$tier" || echo "FAIL $s/references/$tier" + done +done +ls ping-quickstart/references/curated/ | wc -l # Expect: 3 +``` + +Expected: all `OK`, ping-quickstart has 3 curated files. + +- [ ] **Step 5: Commit** + +```bash +git add plugins/ping-identity/skills/ +git commit -m "refactor: align live skills with strategy-doc reference tier model + +Move ping-quickstart's flat references into references/curated/, add +the runtime/ tier with a docs-mcp-routing.md stub to all three live +skills (ping-quickstart, ping-foundation, ping-orchestration), and +update ping-quickstart's SKILL.md paths to match. + +Refs: PLAN.md Phase 0 step 3; strategy doc § 6" +``` + +--- + +### Task 3: Scaffold the three new umbrella skills + +Each new skill ships with `SKILL.md` (≤120 lines, scaffold), `ping-marketplace.json`, and the full `references/{curated,generated,runtime}/` tier. + +**Files (all NEW, three skills × four files each):** +- `plugins/ping-identity/skills/ping-universal-services/{SKILL.md, ping-marketplace.json, references/curated/.gitkeep, references/generated/.gitkeep, references/runtime/docs-mcp-routing.md}` +- `plugins/ping-identity/skills/ping-app-integration/{...same...}` +- `plugins/ping-identity/skills/ping-identity-for-ai/{...same...}` + +- [ ] **Step 1: Create directory trees for the three new skills** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills +for s in ping-universal-services ping-app-integration ping-identity-for-ai; do + mkdir -p $s/references/curated $s/references/generated $s/references/runtime + touch $s/references/curated/.gitkeep $s/references/generated/.gitkeep +done +``` + +- [ ] **Step 2: Define the SKILL.md scaffold template** + +Every new SKILL.md uses this shape (replace `<…>` placeholders per skill). Keep ≤120 lines. + +```markdown +--- +name: +description: +compatibility: Designed for Ping Identity work. References product docs and the Ping Marketplace. +metadata: + publisher: Ping Identity + version: "0.1.0-scaffold" +--- + +# + +> **Status:** Phase 0 scaffold per strategy doc § 4. Body authored in Phase 1. Routing logic stub only. + + + +## Invocation + +Invoke explicitly with `/` or by saying "use to...". + +## When to use this skill + +- +- +- +- +- + +## When NOT to use this skill + +- If the task is platform setup or admin: use `ping-foundation`. +- If the task is flow / journey design: use `ping-orchestration`. +- If the user is just orienting: use `ping-quickstart`. + +## Multi-skill use cases + +A complete use case typically spans: + +| Layer | Skill | +|---|---| +| Platform setup | `ping-foundation` | +| Orchestration / flows | `ping-orchestration` | +| | `` (this skill) | +| App integration | `ping-app-integration` | + +## Routing — Step 1: What are you trying to do? + +| Task | Branch | +|---|---| +| | | + +## Step 2: Platform branch + +| Platform | Curated reference | +|---|---| +| PingOne MT | `references/curated/.md` | +| PingOne ST (AIC) | `references/curated/.md` | +| Ping Software Suite | `references/curated/.md` | + +## Retrieval escalation + +Per strategy doc § 0: + +1. Curated anchors (`references/curated/`) — load 1–3 max. Stop if sufficient. +2. Generated shortlist (`references/generated//top-N.json`) — Phase 2. +3. Docs MCP fallback — see `references/runtime/docs-mcp-routing.md`. Only if curated + shortlist insufficient. +``` + +- [ ] **Step 3: Author `ping-universal-services/SKILL.md`** + +Use the template with: +- `name: ping-universal-services` +- `description`: "Shared services skill for the strategic value layers used across PingOne MT, PingOne ST (AIC), and Ping Software Suite — Protect, Verify, Credentials, IGA, SSO, Authorize. Use this skill whenever a task involves a Universal Service that is consumed from multiple platforms rather than administered from one. Includes service selection guidance, invocation patterns from PingOne or AIC, policy and verification patterns, and cross-product service usage. Also invoke with /ping-universal-services." +- Triggers: "Add PingOne Verify for KYC", "Score risk with PingOne Protect", "Issue a verifiable credential", "Add IGA governance", "Authorize with PingOne Authorize", etc. + +- [ ] **Step 4: Author `ping-app-integration/SKILL.md`** + +- `name: ping-app-integration` +- `description`: "Implementation skill for integrating Ping Identity into web, mobile, and SDK experiences. Use this whenever a task involves Android, iOS, or React SDK integration; embedding journeys; wiring auth flows into a web or mobile app; browser-based redirect flows; orchestration SDK references; or on-prem app-side integration patterns where the primary task is implementation rather than platform administration. Also invoke with /ping-app-integration." +- Triggers: "Integrate Ping into my React app", "Use the iOS SDK with AIC", "Wire OIDC into my mobile app", "Embed a journey in a webview", "Migrate from ForgeRock SDK to Ping SDK", etc. + +- [ ] **Step 5: Author `ping-identity-for-ai/SKILL.md`** + +- `name: ping-identity-for-ai` +- `description`: "AI-era identity patterns: Identity for AI, Verified Trust, agent identity, agent security, AI app authentication patterns, and workforce-helpdesk AI use cases. Use this whenever the task involves giving an AI agent a verified identity, securing agent-to-API access, applying Verified Trust signals, or designing identity for AI workloads. Also invoke with /ping-identity-for-ai." +- Triggers: "Give my AI agent an identity", "Verified Trust signals for an MCP server", "Secure agent access to APIs", "Workforce helpdesk AI auth pattern", "Identity for AI architecture", etc. + +- [ ] **Step 6: Author `ping-marketplace.json` for each new skill** + +Each file follows the existing `ping-marketplace.json` shape from a live skill. Use `/Users/george.bafaloukas/Dev/tiger-agent-skills/plugins/ping-identity/skills/ping-foundation/ping-marketplace.json` as the reference template; substitute name, description, tags per skill. + +- [ ] **Step 7: Author the runtime stub for each new skill** + +Use the same `docs-mcp-routing.md` template from Task 2 Step 3, substituting the skill name. + +- [ ] **Step 8: Verify every SKILL.md is ≤120 lines and `name` matches its directory** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +for s in ping-quickstart ping-foundation ping-orchestration ping-universal-services ping-app-integration ping-identity-for-ai; do + f=plugins/ping-identity/skills/$s/SKILL.md + lines=$(wc -l < $f) + name=$(grep -E '^name:' $f | head -1 | awk '{print $2}') + if [ "$lines" -gt 120 ]; then echo "FAIL: $s SKILL.md is $lines lines (>120)"; fi + if [ "$name" != "$s" ]; then echo "FAIL: $s has name: $name"; fi +done +``` + +Expected: no `FAIL` output. (The three live skills may already exceed 120 lines — if so, leave them for Phase 1 trim work; do not break working content. Print as `WARN` instead and continue.) + +- [ ] **Step 9: Commit** + +```bash +git add plugins/ping-identity/skills/ping-universal-services/ plugins/ping-identity/skills/ping-app-integration/ plugins/ping-identity/skills/ping-identity-for-ai/ +git commit -m "feat: scaffold ping-universal-services, ping-app-integration, ping-identity-for-ai + +Three Phase 0 scaffolds (≤120 lines each, agentskills.io compliant) +matching strategy doc § 4 'In Practice'. Each ships SKILL.md, a +ping-marketplace.json metadata file, and the canonical +references/{curated,generated,runtime}/ tier set per strategy § 6. +Bodies are stubs marked status=Phase 0 scaffold; Phase 1 fills in +routing tables and curated anchors per strategy § 7. + +Refs: PLAN.md Phase 0 step 4; strategy doc § 4 and § 7" +``` + +--- + +### Task 4: Author `rules/runtime-selection.md` + +This is the canonical decision rule for sandbox-vs-production runtime, referenced from every skill's `references/runtime/docs-mcp-routing.md` and used by Layer 3 of the eval (tier discipline scoring). + +**Files:** +- Create: `rules/runtime-selection.md` +- Delete: `rules/.gitkeep` + +- [ ] **Step 1: Author `rules/runtime-selection.md`** + +```markdown +--- +title: Runtime selection — sandbox vs production +status: current +last_updated: 2026-05-29 +--- + +# Runtime selection + +Decision rule: which runtime applies a plan produced by a Ping skill, and which reference tier to load. + +## The two runtime modes + +| Mode | What it does | When applied | +|---|---|---| +| **docs** | Output is a written plan: steps, product names, field tables, links to admin console pages. The user (or another agent) executes manually. | Sandbox/orientation, evaluation, learning, no live tenant available. | +| **helix** | Plan is executed by Helix conversation APIs against a live tenant via product-specific tools. Each write is gated. | Production-bound work, when the user has tenant credentials and explicit intent to apply changes. | + +Helix is **not** a v1 skill — it is a runtime tier referenced from each skill's `references/runtime/docs-mcp-routing.md`. v1.1 may promote Helix to a first-class skill if it passes the four-criteria decision rule in PLAN.md. + +## Decision rule for runtime mode (apply in order) + +1. If the user explicitly asks for **a plan**, **explanation**, **walkthrough**, or **evaluation help** → `docs`. +2. If the user names a **sandbox**, **trial**, **POC**, or asks **"how would I…"** → `docs`. +3. If the user names a **production tenant**, asks to **apply / create / update / configure** in a specific tenant, or invokes a `/ping:` command with `--apply` → `helix`. +4. If the user is **unclear**, default to `docs` and ask one clarifying question. + +## Tier discipline (the strategy doc § 0 "Agent Path" rule) + +Within a `docs` plan, choose the smallest tier that resolves the prompt: + +1. **Curated anchors** (`references/curated/`) — load 1–3. Stop if sufficient. +2. **Generated shortlist** (`references/generated//top-N.json`) — only if curated didn't cover the task. +3. **Docs MCP** (per `references/runtime/docs-mcp-routing.md`) — only if curated + shortlist insufficient. + +Layer 3 of the eval scores **Tier discipline (1–5)**: did the produced plan stop at the smallest sufficient tier per the prompt's `expected_tier`? + +## Anti-patterns + +- ❌ Producing a Helix-style plan when the user is in sandbox mode → over-promises execution. +- ❌ Producing a docs plan when the user explicitly asked to apply → under-delivers. +- ❌ Loading the generated shortlist when one curated anchor is sufficient → wastes tokens. +- ❌ Calling Docs MCP when curated + shortlist would have been enough → wastes tokens and slows the agent. + +## Related + +- `rules/routing-rules.md` — skill selection precedence +- `evals/scorecards/plan-quality-eval.md` — how tier discipline is scored +- Each skill's `references/runtime/docs-mcp-routing.md` +``` + +- [ ] **Step 2: Verify and commit** + +```bash +rm -f /Users/george.bafaloukas/Dev/tiger-agent-skills/rules/.gitkeep +git add rules/runtime-selection.md +git commit -m "docs: add rules/runtime-selection.md + +Canonical decision rule for sandbox (docs) vs production (helix) runtime +plus the strategy-doc § 0 tier-discipline rule (curated → generated → +docs MCP). Referenced from every skill's references/runtime/ +docs-mcp-routing.md and scored by Layer 3 of the eval. + +Refs: PLAN.md Phase 0 step 5; strategy doc § 0 and § 4" +``` + +--- + +### Task 5: Port `authoring-rules.md` and `routing-rules.md` into `rules/`; add Cursor `.mdc` rule + +**Files:** +- Create: `rules/authoring-rules.md` (copied from `shared/templates/AUTHORING-RULES.md`) +- Create: `rules/routing-rules.md` (copied from `shared/taxonomies/routing-rules.md`) +- Create: `rules/ping-identity.mdc` (Cursor-style rule for Cloudflare parity) + +- [ ] **Step 1: Copy authoring rules and routing rules** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +cp shared/templates/AUTHORING-RULES.md rules/authoring-rules.md +cp shared/taxonomies/routing-rules.md rules/routing-rules.md +``` + +- [ ] **Step 2: Author `rules/ping-identity.mdc`** (Cursor's `.mdc` rule format, per Cloudflare's `rules/workers.mdc`) + +```mdc +--- +description: Apply when working on Ping Identity platform tasks (PingOne MT, PingOne ST/AIC, PingFederate, PingAccess, PingDirectory, PingID, DaVinci). +alwaysApply: false +globs: + - "**/*.tsx" + - "**/*.ts" + - "**/*.swift" + - "**/*.kt" + - "**/*.java" + - "**/*.js" + - "**/*.jsx" +--- + +# Ping Identity skill routing + +When the user's task involves Ping Identity platforms, agents have access to six umbrella skills via the agent-skills repo: + +- `ping-quickstart` — front door; product-family detection and routing +- `ping-foundation` — tenant, app, directory, policy, branding, admin +- `ping-orchestration` — DaVinci flows and journeys +- `ping-universal-services` — Protect, Verify, Credentials, IGA, SSO +- `ping-app-integration` — web, mobile, SDK integration +- `ping-identity-for-ai` — AI agent identity, Verified Trust + +## Routing decision order inside every skill + +1. What is the user trying to do? (intent) +2. Which platform family? (PingOne MT / PingOne ST / Ping Software Suite / cross-platform) +3. Which exact product or service? +4. Which reference tier? (curated → generated → docs-mcp) + +## Tier discipline + +Use the smallest trusted context first. Load 1–3 curated anchors before considering the generated shortlist. Use Docs MCP only when the first two tiers are insufficient. +``` + +- [ ] **Step 3: Commit** + +```bash +git add rules/authoring-rules.md rules/routing-rules.md rules/ping-identity.mdc +git commit -m "docs: port authoring-rules and routing-rules into rules/; add Cursor .mdc + +Copy shared/templates/AUTHORING-RULES.md and shared/taxonomies/ +routing-rules.md into rules/ so authors have a single source of truth +at the new repo root. Add rules/ping-identity.mdc (Cursor-style rule) +for parity with Cloudflare's rules/workers.mdc. + +Refs: PLAN.md Phase 0 step 5; Cloudflare repo structure" +``` + +--- + +### Task 6: Refresh `.claude-plugin/marketplace.json` for the v1 skill set + +**Files:** +- Modify: `.claude-plugin/marketplace.json` + +- [ ] **Step 1: Read the current marketplace.json** + +Read `/Users/george.bafaloukas/Dev/tiger-agent-skills/.claude-plugin/marketplace.json`. The existing file already points at `plugins/ping-identity` — only minor metadata refreshes are needed. + +- [ ] **Step 2: Replace the file with v1 content** + +Write `/Users/george.bafaloukas/Dev/tiger-agent-skills/.claude-plugin/marketplace.json`: + +```json +{ + "name": "ping-identity-skills", + "owner": { + "name": "Ping Identity", + "email": "developer-experience@pingidentity.com" + }, + "description": "Agent skills for Ping Identity platforms — six umbrella skills covering PingOne MT, PingOne Advanced Identity Cloud (ST), Ping Software Suite, DaVinci, universal services, app integration, and identity for AI.", + "plugins": [ + { + "name": "ping-identity", + "source": "./plugins/ping-identity", + "displayName": "Ping Identity", + "description": "Six umbrella skills modelled on the Cloudflare few-broad-skills approach: ping-quickstart, ping-foundation, ping-orchestration, ping-universal-services, ping-app-integration, ping-identity-for-ai. Each routes by intent, platform family, exact product/service, and reference tier.", + "version": "1.0.0", + "author": { "name": "Ping Identity" }, + "category": "identity", + "keywords": ["ping", "pingone", "aic", "davinci", "pingfederate", "pingaccess", "identity", "authentication", "ciam", "ai-identity"] + } + ] +} +``` + +- [ ] **Step 3: Validate JSON parses** + +```bash +python3 -c "import json; json.load(open('/Users/george.bafaloukas/Dev/tiger-agent-skills/.claude-plugin/marketplace.json'))" +``` + +Expected: no output (success). + +- [ ] **Step 4: Commit** + +```bash +git add .claude-plugin/marketplace.json +git commit -m "chore: refresh marketplace.json for the 6-umbrella v1 skill set + +Update plugin description and keywords to reflect the strategy-doc § 4 +umbrella set. Source path (./plugins/ping-identity) and version (1.0.0) +unchanged. + +Refs: PLAN.md Phase 0 step 1" +``` + +--- + +### Task 7: Author the eval scorecards + +**Files:** +- Create: `evals/scorecards/routing-eval.md` (ported + extended from `shared/evals/routing-eval.md`) +- Create: `evals/scorecards/anchor-selection-eval.md` (NEW for Layer 2) +- Create: `evals/scorecards/plan-quality-eval.md` (NEW for Layer 3) + +- [ ] **Step 1: Port and update the routing scorecard** + +```bash +cp /Users/george.bafaloukas/Dev/tiger-agent-skills/shared/evals/routing-eval.md /Users/george.bafaloukas/Dev/tiger-agent-skills/evals/scorecards/routing-eval.md +``` + +The existing scorecard already targets the 6 umbrella skills (it's the `shared/evals/` source). Add a new section near the top: + +```markdown +## Layer 1 in the harness + +This scorecard is the human-readable rubric for Layer 1 (routing accuracy). The automated harness implementation is `evals/harness/run_eval.py --layer 1`. It consumes `evals/prompts/.yaml` validated against `evals/schemas/prompt-set.schema.json`. CI runs Layer 1 from Phase 3 onward; PRs failing the pass bar (90% trigger / 90% non-trigger / 80% ambiguous) are blocked. +``` + +- [ ] **Step 2: Author `evals/scorecards/anchor-selection-eval.md`** + +(Same content as in the previous draft — Layer 2 precision/recall against `expected_anchors`. The scorecard explains the algorithm in the same form as Layer 1.) + +```markdown +--- +title: Anchor selection eval — Layer 2 +status: current +last_updated: 2026-05-29 +--- + +# Anchor Selection Eval (Layer 2) + +Used by `evals/harness/run_eval.py --layer 2` to score whether the agent loaded the right curated reference files within an active skill. + +## Inputs + +- `evals/prompts/.yaml` — every `trigger_prompts[*]` entry with non-empty `expected_anchors`. +- The agent's tool-call log for the run, captured by the LLM adapter. + +## Score per prompt + +``` +loaded = set of paths the agent passed to Read(...) +expected = set of paths in expected_anchors +precision = |loaded ∩ expected| / |loaded| +recall = |loaded ∩ expected| / |expected| +pass = recall >= 1.0 AND precision >= 0.5 +``` + +## Aggregate per skill + +- `pass_rate = passed_prompts / total_prompts_with_expected_anchors` +- **Pass bar:** `pass_rate >= 0.85` + +## Failure output + +``` +[FAIL] T-04 (ping-orchestration) + expected: plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md + loaded: plugins/ping-identity/skills/ping-orchestration/references/curated/davinci-flow-patterns.md + recall=0.0 precision=0.0 +``` + +## Common failure modes + +| Failure | Likely cause | Fix | +|---|---|---| +| recall=0 always | SKILL.md doesn't reference the expected anchor by path | Add anchor path to the SKILL.md retrieval list | +| recall=1 but precision<0.5 | SKILL.md loads too many anchors per prompt | Tighten routing table; split anchors | +| Inconsistent across runs | LLM non-determinism | Run with temperature=0; if still inconsistent, reword SKILL.md | +``` + +- [ ] **Step 3: Author `evals/scorecards/plan-quality-eval.md`** (Layer 3, LLM-as-judge — same content as previous draft, with the rubric updated to score `tier_discipline` rather than `runtime_correctness`) + +```markdown +--- +title: Plan quality eval — Layer 3 +status: current +last_updated: 2026-05-29 +--- + +# Plan Quality Eval (Layer 3) + +Used by `evals/harness/judge_plans.py` to score the textual plan a skill produces against a hand-authored golden plan. + +## Inputs + +- `evals/prompts/.yaml` — `trigger_prompts[*]` with a corresponding `evals/golden//.md` file. +- The full final assistant message from the skill being evaluated. +- A judge LLM (Claude / Codex / Gemini), DIFFERENT from the LLM under test. + +## Judge prompt template + +``` +You are scoring an agent's response against a reference plan. + +USER PROMPT: + + +REFERENCE PLAN (golden): + + +AGENT'S PRODUCED PLAN: + + +Score 1–5 on each dimension. Output ONLY valid JSON: +{ + "correctness": {"score": , "reason": "<≤200 chars>"}, + "completeness": {"score": , "reason": "<≤200 chars>"}, + "concreteness": {"score": , "reason": "<≤200 chars>"}, + "tier_discipline": {"score": , "reason": "<≤200 chars>", "expected": "", "observed": ""} +} + +Rubric: +- correctness: factual accuracy about Ping products, services, fields +- completeness: covers every required step in the reference +- concreteness: names specific products, fields, env vars; no generic prose +- tier_discipline: matches expected_tier per rules/runtime-selection.md; + did not over-escalate; did not under-load +``` + +## Aggregate per skill + +``` +mean_correctness = mean of all correctness scores +mean_completeness = mean of all completeness scores +mean_concreteness = mean of all concreteness scores +mean_tier = mean of all tier_discipline scores + +pass = all four means >= 4.0 AND no individual score < 3 +``` + +## Why a different judge model + +Self-judging collapses the eval. Defaults: +- Claude under test → Codex judges +- Codex under test → Claude judges +- Gemini under test → Claude judges + +Override with `--judge `. +``` + +- [ ] **Step 4: Commit** + +```bash +git add evals/scorecards/ +git commit -m "docs: add Layer 1, 2, 3 eval scorecards + +Port shared/evals/routing-eval.md to evals/scorecards/ and add Layer 2 +(anchor selection — precision/recall against expected_anchors) and +Layer 3 (plan quality — LLM-as-judge across correctness, completeness, +concreteness, tier discipline). + +Refs: PLAN.md § Evaluation; strategy doc § 0" +``` + +--- + +### Task 8: Author the prompt-set JSON Schema and validator (TDD) + +**Files:** +- Create: `evals/schemas/prompt-set.schema.json` +- Create: `evals/harness/validate_prompts.py` +- Create: `evals/harness/tests/test_validate_prompts.py` +- Create: `evals/harness/pyproject.toml` +- Create: `evals/harness/requirements.txt` +- Create: `evals/__init__.py`, `evals/harness/__init__.py`, `evals/harness/adapters/__init__.py`, `evals/harness/tests/__init__.py` + +- [ ] **Step 1: Write the failing test** + +`/Users/george.bafaloukas/Dev/tiger-agent-skills/evals/harness/tests/test_validate_prompts.py`: + +```python +"""Schema validation tests for evals/prompts/*.yaml.""" +import textwrap +from pathlib import Path + +import pytest + +from evals.harness.validate_prompts import ValidationError, validate_prompt_file + + +def write_yaml(tmp_path: Path, name: str, content: str) -> Path: + p = tmp_path / name + p.write_text(textwrap.dedent(content).strip() + "\n") + return p + + +def test_minimal_valid_prompt_set_passes(tmp_path: Path): + f = write_yaml(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "Where do I start with Ping Identity?" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: + - id: N-01 + prompt: "Build me a journey in PingOne ST." + expected_skill: ping-orchestration + ambiguous_prompts: + - id: A-01 + prompt: "I want to add MFA." + expected_clarification_keywords: ["pingone", "aic"] + """) + validate_prompt_file(f) # no exception = pass + + +def test_skill_field_must_match_filename(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-orchestration + version: 1 + trigger_prompts: [] + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="skill .* must match filename"): + validate_prompt_file(f) + + +def test_missing_required_top_level_field_fails(tmp_path: Path): + f = write_yaml(tmp_path, "ping-orchestration.yaml", """ + skill: ping-orchestration + trigger_prompts: [] + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="version"): + validate_prompt_file(f) + + +def test_trigger_prompt_requires_id_and_prompt(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-foundation + version: 1 + trigger_prompts: + - prompt: "missing id" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="id"): + validate_prompt_file(f) + + +def test_expected_tier_enum_enforced(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-foundation + version: 1 + trigger_prompts: + - id: T-01 + prompt: "test prompt for tier" + expected_anchors: [] + expected_tier: invalid-value + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="expected_tier"): + validate_prompt_file(f) +``` + +- [ ] **Step 2: Run the tests and verify they fail** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m pytest evals/harness/tests/test_validate_prompts.py -v +``` + +Expected: 5 failures (module `evals.harness.validate_prompts` not found). + +- [ ] **Step 3: Write `evals/schemas/prompt-set.schema.json`** + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Skill prompt set", + "type": "object", + "required": ["skill", "version", "trigger_prompts", "non_trigger_prompts", "ambiguous_prompts"], + "additionalProperties": false, + "properties": { + "skill": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" }, + "version": { "type": "integer", "minimum": 1 }, + "trigger_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_anchors", "expected_tier"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^T-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 10 }, + "expected_anchors": { "type": "array", "items": { "type": "string" } }, + "expected_tier": { "type": "string", "enum": ["curated", "generated", "docs-mcp"] }, + "notes": { "type": "string" } + } + } + }, + "non_trigger_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_skill"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^N-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 10 }, + "expected_skill": { "type": ["string", "null"] }, + "notes": { "type": "string" } + } + } + }, + "ambiguous_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_clarification_keywords"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^A-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 5 }, + "expected_clarification_keywords": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "notes": { "type": "string" } + } + } + } + } +} +``` + +- [ ] **Step 4: Implement `evals/harness/validate_prompts.py`** + +```python +"""Validate evals/prompts/*.yaml files against the prompt-set JSON schema.""" +from __future__ import annotations + +import json +import sys +from pathlib import Path +from typing import Iterable + +import yaml +from jsonschema import Draft7Validator + +REPO_ROOT = Path(__file__).resolve().parents[2] +SCHEMA_PATH = REPO_ROOT / "evals" / "schemas" / "prompt-set.schema.json" +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" + + +class ValidationError(Exception): + pass + + +def _load_schema() -> dict: + with SCHEMA_PATH.open() as f: + return json.load(f) + + +def validate_prompt_file(path: Path) -> None: + with path.open() as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValidationError(f"{path}: top-level must be a mapping") + + schema = _load_schema() + errors = sorted(Draft7Validator(schema).iter_errors(data), key=lambda e: e.path) + if errors: + msgs = "; ".join(f"{'/'.join(map(str, e.path)) or ''}: {e.message}" for e in errors) + raise ValidationError(f"{path}: {msgs}") + + expected_skill = path.stem + if data["skill"] != expected_skill: + raise ValidationError( + f"{path}: skill '{data['skill']}' must match filename '{expected_skill}'" + ) + + +def validate_all(paths: Iterable[Path] | None = None) -> list[Path]: + paths = list(paths) if paths is not None else sorted(PROMPTS_DIR.glob("*.yaml")) + failed: list[Path] = [] + for p in paths: + try: + validate_prompt_file(p) + print(f"OK {p.relative_to(REPO_ROOT)}") + except ValidationError as exc: + failed.append(p) + print(f"FAIL {exc}", file=sys.stderr) + return failed + + +def main() -> int: + failed = validate_all() + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 5: Add `__init__.py` files and dependency manifests** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +touch evals/__init__.py evals/harness/__init__.py evals/harness/adapters/__init__.py evals/harness/tests/__init__.py +``` + +`evals/harness/requirements.txt`: +``` +pyyaml>=6.0 +jsonschema>=4.0 +pytest>=8.0 +anthropic>=0.40 +``` + +`evals/harness/pyproject.toml`: +```toml +[tool.pytest.ini_options] +testpaths = ["evals/harness/tests"] +pythonpath = ["."] +``` + +- [ ] **Step 6: Install deps and run tests** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m pip install -r evals/harness/requirements.txt +python3 -m pytest evals/harness/tests/test_validate_prompts.py -v +``` + +Expected: 5 passed. + +- [ ] **Step 7: Commit** + +```bash +git add evals/__init__.py evals/schemas/prompt-set.schema.json evals/harness/__init__.py evals/harness/adapters/__init__.py evals/harness/tests/__init__.py evals/harness/validate_prompts.py evals/harness/tests/test_validate_prompts.py evals/harness/requirements.txt evals/harness/pyproject.toml +git commit -m "feat(evals): prompt-set schema and validator + +JSON Schema for evals/prompts/*.yaml + Python validator with 5 pytest +cases covering: minimal valid set, skill/filename match, required +top-level fields, required item fields, tier enum. + +Run: python -m evals.harness.validate_prompts + +Refs: PLAN.md § Evaluation Layer 1" +``` + +--- + +### Task 9: Author the 6 prompt-set YAML files + +The three live skills (`ping-quickstart`, `ping-foundation`, `ping-orchestration`) ship at v1 minimums (≥10 / ≥5 / ≥3) since their bodies and curated content already exist. The three new skills ship Phase-0 minimums (3 / 2 / 1) and grow in Phase 1. + +**Files (all NEW):** +- `evals/prompts/ping-quickstart.yaml` (10 / 5 / 3) +- `evals/prompts/ping-foundation.yaml` (10 / 5 / 3) +- `evals/prompts/ping-orchestration.yaml` (10 / 5 / 3) +- `evals/prompts/ping-universal-services.yaml` (3 / 2 / 1) +- `evals/prompts/ping-app-integration.yaml` (3 / 2 / 1) +- `evals/prompts/ping-identity-for-ai.yaml` (3 / 2 / 1) + +- [ ] **Step 1: Author `evals/prompts/ping-quickstart.yaml`** + +10 trigger prompts mining the existing curated content (`getting-started-overview`, `choose-the-right-ping-platform`, `common-starting-patterns`). Examples: "Where do I start?", "PingOne vs PingFederate?", "We're evaluating Ping", "I inherited a Ping deployment", "Should we use cloud or on-prem?", "We want CIAM where do we begin?", "Add MFA without a platform context", "ForgeRock migration starting point", "Identity verification for new product", "Help us choose between AIC and PingOne MT". + +Each `expected_anchors` references the seeded curated files at `plugins/ping-identity/skills/ping-quickstart/references/curated/.md`. + +5 non-trigger: prompts that name a platform directly and should route to the relevant umbrella, not quickstart. Examples: "Build me a DaVinci flow" (→ orchestration), "Add an OIDC app to my AIC tenant" (→ foundation), "Integrate Ping into my React app" (→ app-integration), "Score risk with PingOne Protect" (→ universal-services), "Give my AI agent a verified identity" (→ identity-for-ai). + +3 ambiguous: "I want to add MFA", "How do I do passwordless?", "Risk-based authentication"; each lists keywords covering platform + workforce/CIAM + sandbox/production. + +Skeleton (full file written by the executing agent): + +```yaml +skill: ping-quickstart +version: 1 +trigger_prompts: + - id: T-01 + prompt: "Where do I start with Ping Identity? We're new and don't know which product to pick." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + expected_tier: curated + # T-02 ... T-10 +non_trigger_prompts: + - id: N-01 + prompt: "Build me a registration journey in PingOne ST that collects email and sends an OTP." + expected_skill: ping-orchestration + # N-02 ... N-05 +ambiguous_prompts: + - id: A-01 + prompt: "I want to add MFA." + expected_clarification_keywords: ["pingone", "aic", "platform", "workforce", "ciam"] + # A-02, A-03 +``` + +- [ ] **Step 2: Author `evals/prompts/ping-foundation.yaml`** + +10 trigger covering: PingOne MT environment provisioning, AIC tenant + realm setup, OIDC app registration, SAML SP connection on PingFederate, directory setup (managed objects, LDAP/AD), authentication policy, branding/themes, PingDirectory admin, PingID administration, identity data model. + +`expected_anchors` reference the existing curated files at `plugins/ping-identity/skills/ping-foundation/references/curated//.md`. + +5 non-trigger: orchestration prompt, app integration prompt, AI identity prompt, universal services prompt, "where do I start" prompt. + +3 ambiguous: "Configure MFA" (policy in foundation, or step-up in orchestration?), "Set up SSO" (which platform?), "Add a user" (admin or identity service?). + +- [ ] **Step 3: Author `evals/prompts/ping-orchestration.yaml`** + +10 trigger covering the existing curated content: DaVinci flow patterns, journey design patterns, the six node-category files (basic-auth, mfa, identity-management, federation-contextual, risk-management, utility), and the eight journey-use-case files (passwordless registration, social/local registration, password reset, progressive profiling, account recovery, MFA multi-method, financial step-up, Protect risk integration). + +5 non-trigger / 3 ambiguous as appropriate. + +- [ ] **Step 4: Author `evals/prompts/ping-universal-services.yaml`** (3 / 2 / 1) + +Trigger: "Add PingOne Verify for KYC", "Score risk with PingOne Protect during login", "Issue a verifiable credential to a user". `expected_anchors: []` for now with `notes: "Phase 1 will populate expected_anchors once curated anchors exist."`. `expected_tier: curated`. + +Non-trigger: a foundation prompt, an app-integration prompt. + +Ambiguous: 1 prompt naming "verification" without specifying KYC vs. MFA verification. + +- [ ] **Step 5: Author `evals/prompts/ping-app-integration.yaml`** (3 / 2 / 1) + +Trigger: "Integrate Ping into my React app via the orchestration SDK", "Use the iOS SDK with AIC journeys", "Wire OIDC redirect into a mobile app". + +Non-trigger: a foundation prompt, a quickstart prompt. + +Ambiguous: 1 prompt naming "SDK" without specifying iOS / Android / React. + +- [ ] **Step 6: Author `evals/prompts/ping-identity-for-ai.yaml`** (3 / 2 / 1) + +Trigger: "Give my AI agent a verified identity for API access", "Use Verified Trust signals in my MCP server", "Workforce helpdesk AI auth pattern". + +Non-trigger: a foundation prompt, an orchestration prompt. + +Ambiguous: 1 prompt naming "agent" without specifying AI agent vs. user agent. + +- [ ] **Step 7: Validate every prompt YAML** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m evals.harness.validate_prompts +``` + +Expected: +``` +OK evals/prompts/ping-app-integration.yaml +OK evals/prompts/ping-foundation.yaml +OK evals/prompts/ping-identity-for-ai.yaml +OK evals/prompts/ping-orchestration.yaml +OK evals/prompts/ping-quickstart.yaml +OK evals/prompts/ping-universal-services.yaml +``` + +- [ ] **Step 8: Commit** + +```bash +git add evals/prompts/ +git commit -m "feat(evals): seed prompt sets for the 6 v1 umbrella skills + +Live skills (ping-quickstart, ping-foundation, ping-orchestration) ship +at v1 minimums (10/5/3) since their bodies and curated content already +exist. The three new skills ship Phase-0 minimums (3/2/1) and expand in +Phase 1 once curated anchors land per strategy doc § 7. + +All prompt sets validate against evals/schemas/prompt-set.schema.json. + +Refs: PLAN.md § Evaluation, Phase 0 step 4" +``` + +--- + +### Task 10: Build the LLM adapter base + mock adapter (TDD) + +Same content as the previous draft. The mock adapter is what makes the harness testable without API budget. + +**Files:** +- Create: `evals/harness/adapters/base.py` +- Create: `evals/harness/adapters/mock.py` +- Create: `evals/harness/tests/test_adapters_mock.py` + +- [ ] **Step 1: Write the failing test** + +`/Users/george.bafaloukas/Dev/tiger-agent-skills/evals/harness/tests/test_adapters_mock.py`: + +```python +from evals.harness.adapters.mock import MockAdapter, MockRule + + +def test_mock_adapter_returns_loaded_skills_per_rule(): + rules = [ + MockRule(prompt_contains="MFA", loaded_skills=["ping-quickstart"], read_paths=[]), + MockRule(prompt_contains="journey", loaded_skills=["ping-orchestration"], + read_paths=["plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md"]), + ] + adapter = MockAdapter(rules=rules, default_skills=[]) + + r1 = adapter.run("I want to add MFA to my mobile banking app.") + assert r1.loaded_skills == ["ping-quickstart"] + assert r1.read_paths == [] + assert r1.final_message != "" + + r2 = adapter.run("Build a registration journey with email OTP.") + assert r2.loaded_skills == ["ping-orchestration"] + assert r2.read_paths == [ + "plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md" + ] + + +def test_mock_adapter_default_when_no_rule_matches(): + adapter = MockAdapter(rules=[], default_skills=[]) + r = adapter.run("a prompt that matches nothing") + assert r.loaded_skills == [] + assert r.read_paths == [] + + +def test_mock_adapter_supports_clarification_for_ambiguous_prompts(): + rules = [ + MockRule(prompt_contains="add MFA", + clarifying_question="Are you in PingOne MT, AIC, or on-prem? Workforce or CIAM?"), + ] + adapter = MockAdapter(rules=rules, default_skills=[]) + r = adapter.run("I want to add MFA.") + assert r.loaded_skills == [] + assert r.clarifying_question is not None + assert "pingone" in r.clarifying_question.lower() or "ciam" in r.clarifying_question.lower() or "aic" in r.clarifying_question.lower() +``` + +- [ ] **Step 2: Run the tests and verify they fail** + +```bash +python3 -m pytest evals/harness/tests/test_adapters_mock.py -v +``` + +Expected: 3 failures (module not found). + +- [ ] **Step 3: Implement `evals/harness/adapters/base.py`** + +```python +"""Adapter base — every LLM driver implements this.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Protocol + + +@dataclass +class RunResult: + loaded_skills: list[str] = field(default_factory=list) + read_paths: list[str] = field(default_factory=list) + final_message: str = "" + clarifying_question: str | None = None + raw_trace: list[dict] = field(default_factory=list) + + +class LLMAdapter(Protocol): + def run(self, prompt: str) -> RunResult: ... +``` + +- [ ] **Step 4: Implement `evals/harness/adapters/mock.py`** + +```python +"""Deterministic mock adapter for harness self-tests.""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from evals.harness.adapters.base import RunResult + + +@dataclass +class MockRule: + prompt_contains: str + loaded_skills: list[str] = field(default_factory=list) + read_paths: list[str] = field(default_factory=list) + clarifying_question: str | None = None + final_message: str = "[mock plan]" + + +class MockAdapter: + def __init__(self, rules: list[MockRule], default_skills: list[str] | None = None): + self.rules = rules + self.default_skills = default_skills or [] + + def run(self, prompt: str) -> RunResult: + for rule in self.rules: + if rule.prompt_contains.lower() in prompt.lower(): + return RunResult( + loaded_skills=list(rule.loaded_skills), + read_paths=list(rule.read_paths), + final_message=rule.final_message, + clarifying_question=rule.clarifying_question, + ) + return RunResult(loaded_skills=list(self.default_skills)) +``` + +- [ ] **Step 5: Run the tests** + +```bash +python3 -m pytest evals/harness/tests/test_adapters_mock.py -v +``` + +Expected: 3 passed. + +- [ ] **Step 6: Commit** + +```bash +git add evals/harness/adapters/ evals/harness/tests/test_adapters_mock.py +git commit -m "feat(evals): LLMAdapter protocol + deterministic mock adapter + +base.py defines the RunResult dataclass and LLMAdapter Protocol every +driver must satisfy. mock.py provides a rule-based adapter so harness +unit tests can assert scoring without hitting a real LLM API. + +Refs: PLAN.md § Evaluation Layer 4 (cross-LLM)" +``` + +--- + +### Task 11: Build `evals/harness/run_eval.py` Layer 1 + Layer 2 runner (TDD) + +Same content as the previous draft, with paths updated to the strategy-doc layout (`plugins/ping-identity/skills//references/curated/...`). + +**Files:** +- Create: `evals/harness/run_eval.py` +- Create: `evals/harness/tests/test_run_eval.py` + +- [ ] **Step 1: Write the failing test** + +`/Users/george.bafaloukas/Dev/tiger-agent-skills/evals/harness/tests/test_run_eval.py`: + +```python +from pathlib import Path +import textwrap + +from evals.harness.adapters.mock import MockAdapter, MockRule +from evals.harness.run_eval import score_layer_1, score_layer_2, load_prompt_set + + +def write(tmp_path: Path, name: str, content: str) -> Path: + p = tmp_path / name + p.write_text(textwrap.dedent(content).strip() + "\n") + return p + + +def test_layer1_trigger_correct_when_skill_loaded(tmp_path): + f = write(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "where do I start with Ping?" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: + - id: N-01 + prompt: "build me a journey" + expected_skill: ping-orchestration + ambiguous_prompts: + - id: A-01 + prompt: "I want MFA" + expected_clarification_keywords: ["pingone", "aic"] + """) + pset = load_prompt_set(f) + adapter = MockAdapter(rules=[ + MockRule(prompt_contains="where do I start", loaded_skills=["ping-quickstart"]), + MockRule(prompt_contains="build me a journey", loaded_skills=["ping-orchestration"]), + MockRule(prompt_contains="I want MFA", clarifying_question="pingone aic mt or st?"), + ]) + report = score_layer_1(pset, adapter) + assert report.trigger_pass_rate == 1.0 + assert report.non_trigger_pass_rate == 1.0 + assert report.ambiguous_pass_rate == 1.0 + assert report.passed_overall is True + + +def test_layer1_trigger_fails_when_wrong_skill_loaded(tmp_path): + f = write(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "where do I start" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + pset = load_prompt_set(f) + adapter = MockAdapter(rules=[ + MockRule(prompt_contains="where do I start", loaded_skills=["ping-foundation"]), + ]) + report = score_layer_1(pset, adapter) + assert report.trigger_pass_rate == 0.0 + assert report.passed_overall is False + assert "T-01" in report.failures[0] + + +def test_layer2_recall_and_precision(tmp_path): + f = write(tmp_path, "ping-orchestration.yaml", """ + skill: ping-orchestration + version: 1 + trigger_prompts: + - id: T-01 + prompt: "build a journey" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + pset = load_prompt_set(f) + + adapter_pass = MockAdapter(rules=[MockRule( + prompt_contains="build a journey", + loaded_skills=["ping-orchestration"], + read_paths=["plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md"], + )]) + rep = score_layer_2(pset, adapter_pass) + assert rep.pass_rate == 1.0 + + adapter_miss = MockAdapter(rules=[MockRule( + prompt_contains="build a journey", + loaded_skills=["ping-orchestration"], + read_paths=["plugins/ping-identity/skills/ping-orchestration/references/curated/davinci-flow-patterns.md"], + )]) + rep = score_layer_2(pset, adapter_miss) + assert rep.pass_rate == 0.0 +``` + +- [ ] **Step 2: Run and verify the tests fail** + +```bash +python3 -m pytest evals/harness/tests/test_run_eval.py -v +``` + +Expected: 3 failures (module not found). + +- [ ] **Step 3: Implement `evals/harness/run_eval.py`** + +```python +"""Layer 1 (routing) + Layer 2 (anchor selection) eval runner. + +Usage: + python -m evals.harness.run_eval --adapter mock --layer 1 + python -m evals.harness.run_eval --adapter claude --layer 2 --skill ping-orchestration +""" +from __future__ import annotations + +import argparse +import json +import sys +from dataclasses import dataclass, field +from datetime import date +from pathlib import Path + +import yaml + +from evals.harness.adapters.base import LLMAdapter +from evals.harness.adapters.mock import MockAdapter, MockRule + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" +RESULTS_DIR = REPO_ROOT / "evals" / "results" + +LAYER_1_TRIGGER_BAR = 0.90 +LAYER_1_NON_TRIGGER_BAR = 0.90 +LAYER_1_AMBIGUOUS_BAR = 0.80 +LAYER_2_BAR = 0.85 + + +@dataclass +class PromptSet: + skill: str + trigger: list[dict] + non_trigger: list[dict] + ambiguous: list[dict] + + +@dataclass +class Layer1Report: + skill: str + trigger_pass_rate: float + non_trigger_pass_rate: float + ambiguous_pass_rate: float + failures: list[str] = field(default_factory=list) + + @property + def passed_overall(self) -> bool: + return ( + self.trigger_pass_rate >= LAYER_1_TRIGGER_BAR + and self.non_trigger_pass_rate >= LAYER_1_NON_TRIGGER_BAR + and self.ambiguous_pass_rate >= LAYER_1_AMBIGUOUS_BAR + ) + + +@dataclass +class Layer2Report: + skill: str + pass_rate: float + failures: list[str] = field(default_factory=list) + + @property + def passed_overall(self) -> bool: + return self.pass_rate >= LAYER_2_BAR + + +def load_prompt_set(path: Path) -> PromptSet: + with path.open() as f: + data = yaml.safe_load(f) + return PromptSet( + skill=data["skill"], + trigger=data["trigger_prompts"], + non_trigger=data["non_trigger_prompts"], + ambiguous=data["ambiguous_prompts"], + ) + + +def _safe_rate(passed: int, total: int) -> float: + return 1.0 if total == 0 else passed / total + + +def score_layer_1(pset: PromptSet, adapter: LLMAdapter) -> Layer1Report: + failures: list[str] = [] + + t_pass = 0 + for p in pset.trigger: + result = adapter.run(p["prompt"]) + if pset.skill in result.loaded_skills: + t_pass += 1 + else: + failures.append(f"[trigger] {p['id']} expected {pset.skill}, got {result.loaded_skills}") + + n_pass = 0 + for p in pset.non_trigger: + result = adapter.run(p["prompt"]) + if pset.skill not in result.loaded_skills: + n_pass += 1 + else: + failures.append(f"[non-trigger] {p['id']} {pset.skill} should NOT load") + + a_pass = 0 + for p in pset.ambiguous: + result = adapter.run(p["prompt"]) + cq = (result.clarifying_question or "").lower() + keywords = p["expected_clarification_keywords"] + if cq and any(k.lower() in cq for k in keywords): + a_pass += 1 + else: + failures.append(f"[ambiguous] {p['id']} expected clarification with one of {keywords}") + + return Layer1Report( + skill=pset.skill, + trigger_pass_rate=_safe_rate(t_pass, len(pset.trigger)), + non_trigger_pass_rate=_safe_rate(n_pass, len(pset.non_trigger)), + ambiguous_pass_rate=_safe_rate(a_pass, len(pset.ambiguous)), + failures=failures, + ) + + +def score_layer_2(pset: PromptSet, adapter: LLMAdapter) -> Layer2Report: + failures: list[str] = [] + counted = 0 + passed = 0 + + for p in pset.trigger: + expected = set(p.get("expected_anchors") or []) + if not expected: + continue + counted += 1 + result = adapter.run(p["prompt"]) + loaded = set(result.read_paths or []) + recall = len(loaded & expected) / len(expected) + precision = (len(loaded & expected) / len(loaded)) if loaded else 0.0 + if recall >= 1.0 and precision >= 0.5: + passed += 1 + else: + failures.append( + f"{p['id']} expected={sorted(expected)} loaded={sorted(loaded)} " + f"recall={recall:.2f} precision={precision:.2f}" + ) + + return Layer2Report(skill=pset.skill, pass_rate=_safe_rate(passed, counted), failures=failures) + + +def _build_adapter(name: str) -> LLMAdapter: + if name == "mock": + rules = [] + for prompt_file in sorted(PROMPTS_DIR.glob("*.yaml")): + pset = load_prompt_set(prompt_file) + for p in pset.trigger: + rules.append(MockRule( + prompt_contains=p["prompt"][:30], + loaded_skills=[pset.skill], + read_paths=p.get("expected_anchors") or [], + )) + for p in pset.ambiguous: + rules.append(MockRule( + prompt_contains=p["prompt"][:30], + clarifying_question=" ".join(p["expected_clarification_keywords"]), + )) + return MockAdapter(rules=rules) + if name == "claude": + from evals.harness.adapters.claude import ClaudeAdapter + return ClaudeAdapter() + raise SystemExit(f"unknown adapter: {name}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--adapter", default="mock", choices=["mock", "claude"]) + parser.add_argument("--layer", type=int, choices=[1, 2], default=1) + parser.add_argument("--skill", default=None) + parser.add_argument("--write-results", action="store_true") + args = parser.parse_args() + + adapter = _build_adapter(args.adapter) + files = sorted(PROMPTS_DIR.glob("*.yaml")) + if args.skill: + files = [f for f in files if f.stem == args.skill] + + all_pass = True + out: dict[str, dict] = {} + + for f in files: + pset = load_prompt_set(f) + if args.layer == 1: + r = score_layer_1(pset, adapter) + print(f"[L1] {pset.skill} trigger={r.trigger_pass_rate:.0%} " + f"non_trigger={r.non_trigger_pass_rate:.0%} ambiguous={r.ambiguous_pass_rate:.0%} " + f"{'PASS' if r.passed_overall else 'FAIL'}") + for line in r.failures: + print(f" {line}") + all_pass = all_pass and r.passed_overall + out[pset.skill] = { + "layer": 1, + "trigger": r.trigger_pass_rate, + "non_trigger": r.non_trigger_pass_rate, + "ambiguous": r.ambiguous_pass_rate, + "passed": r.passed_overall, + } + else: + r = score_layer_2(pset, adapter) + print(f"[L2] {pset.skill} pass_rate={r.pass_rate:.0%} " + f"{'PASS' if r.passed_overall else 'FAIL'}") + for line in r.failures: + print(f" {line}") + all_pass = all_pass and r.passed_overall + out[pset.skill] = {"layer": 2, "pass_rate": r.pass_rate, "passed": r.passed_overall} + + if args.write_results: + day = RESULTS_DIR / date.today().isoformat() + day.mkdir(parents=True, exist_ok=True) + (day / f"{args.adapter}.layer{args.layer}.json").write_text(json.dumps(out, indent=2)) + + return 0 if all_pass else 1 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 4: Run all harness unit tests** + +```bash +python3 -m pytest evals/harness/tests/ -v +``` + +Expected: all 11 tests pass. + +- [ ] **Step 5: Run the harness end-to-end with the mock adapter** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m evals.harness.run_eval --adapter mock --layer 1 +python3 -m evals.harness.run_eval --adapter mock --layer 2 +``` + +Expected: prints `[L1] ... PASS` and `[L2] ... PASS` for each of the 6 skills, exits 0. + +- [ ] **Step 6: Commit** + +```bash +git add evals/harness/run_eval.py evals/harness/tests/test_run_eval.py +git commit -m "feat(evals): Layer 1 + Layer 2 runner with mock-adapter end-to-end + +run_eval.py loads evals/prompts/*.yaml, drives an LLMAdapter (mock or +claude), and scores routing accuracy (Layer 1) or anchor selection +(Layer 2) against the bars in evals/scorecards/. Three pytest cases +cover trigger pass, trigger fail, and Layer 2 recall/precision. Mock +adapter makes the harness self-testing without API budget. + +Run: + python -m evals.harness.run_eval --adapter mock --layer 1 + python -m evals.harness.run_eval --adapter mock --layer 2 + +Refs: PLAN.md § Evaluation Layers 1 and 2" +``` + +--- + +### Task 12: Stub the Layer 3 (judge) and Layer 4 (cross-LLM) drivers + +**Files:** +- Create: `evals/harness/judge_plans.py` +- Create: `evals/harness/cross_llm.py` +- Create: `evals/harness/adapters/claude.py` (stub) + +- [ ] **Step 1: Write `evals/harness/adapters/claude.py`** (stub) + +```python +"""Claude (Anthropic) adapter — Phase 1 implementation. + +Phase 0 ships this as a stub so `--adapter claude` exits with a clear, +actionable message rather than an ImportError. +""" +from __future__ import annotations + +import os + +from evals.harness.adapters.base import RunResult + + +class ClaudeAdapter: + def __init__(self) -> None: + if not os.environ.get("ANTHROPIC_API_KEY"): + raise SystemExit( + "ANTHROPIC_API_KEY not set. Phase 1 wires real Claude calls — " + "for now the mock adapter is the only fully implemented driver." + ) + raise SystemExit( + "ClaudeAdapter is a Phase 0 stub. Phase 1 implements: " + "register skills/* as available skills, capture Skill() and Read() " + "tool calls, return RunResult." + ) + + def run(self, prompt: str) -> RunResult: + raise NotImplementedError +``` + +- [ ] **Step 2: Write `evals/harness/judge_plans.py`** (Layer 3 stub) + +```python +"""Layer 3 — Plan quality eval (LLM-as-judge). + +Phase 0: parses prompt sets and golden plans, prints what it WOULD send +to the judge. Phase 1: wires in actual judge calls. +""" +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" +GOLDEN_DIR = REPO_ROOT / "evals" / "golden" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--skill", required=True) + parser.add_argument("--judge", default="claude", choices=["claude", "codex", "gemini"]) + args = parser.parse_args() + + pf = PROMPTS_DIR / f"{args.skill}.yaml" + if not pf.exists(): + print(f"no prompt set for {args.skill}", file=sys.stderr) + return 1 + + pset = yaml.safe_load(pf.read_text()) + gdir = GOLDEN_DIR / args.skill + if not gdir.exists(): + print(f"[stub] no goldens at {gdir} — Phase 1 authors them. Skipping.") + return 0 + + for prompt in pset["trigger_prompts"]: + gp = gdir / f"{prompt['id']}.md" + if not gp.exists(): + print(f"[skip] {prompt['id']} — no golden") + continue + print(f"[would-judge] skill={args.skill} prompt={prompt['id']} judge={args.judge} " + f"golden_chars={len(gp.read_text())}") + + print("\n[stub] Phase 1 wires the judge LLM call. See evals/scorecards/plan-quality-eval.md.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 3: Write `evals/harness/cross_llm.py`** (Layer 4 stub) + +```python +"""Layer 4 — Cross-LLM consistency. + +Phase 0 stub: lists what would be run. Phase 1 wires in real adapters. +""" +from __future__ import annotations + +import argparse +import sys + +LLMS = ["claude", "codex", "gemini"] + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--layer", type=int, choices=[1, 3], default=1) + args = parser.parse_args() + + print(f"[stub] cross-LLM Layer {args.layer} run plan:") + for llm in LLMS: + print(f" - {llm}: would call run_eval.py / judge_plans.py with --adapter {llm}") + print("\n[stub] Phase 1 wires real adapters.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) +``` + +- [ ] **Step 4: Smoke-run the stubs** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m evals.harness.judge_plans --skill ping-orchestration +python3 -m evals.harness.cross_llm --layer 1 +``` + +Expected: both print `[stub]` lines and exit 0. + +- [ ] **Step 5: Commit** + +```bash +git add evals/harness/judge_plans.py evals/harness/cross_llm.py evals/harness/adapters/claude.py +git commit -m "feat(evals): scaffold Layer 3 judge and Layer 4 cross-LLM stubs + +Both runnable, both exit 0 with [stub] messages so Phase 1 can wire in +real LLM calls without churning file layout. ClaudeAdapter raises a +clear SystemExit until Phase 1 implements it. + +Refs: PLAN.md § Evaluation Layers 3 and 4" +``` + +--- + +### Task 13: Author `evals/README.md` and run the Phase 0 exit-criteria smoke check + +**Files:** +- Create: `evals/README.md` + +- [ ] **Step 1: Author `evals/README.md`** (same content as previous draft, with paths updated to the strategy-doc layout) + +```markdown +# Skill Evaluation Harness + +Measures skill accuracy across 5 layers. Layer 1 + Layer 2 are runnable +in Phase 0; Layer 3, 4, 5 are scaffolded and wired up in Phase 1+. + +Strategy doc § 2.7: "Testing and eval frameworks are a progressive +evolution, not a day-1 requirement. We keep the investment low." + +## What gets measured + +| Layer | Question | Bar | When | +|---|---|---|---| +| 1 — Routing | Right umbrella skill activated? | 90% trigger / 90% non-trigger / 80% ambiguous | Every PR (Phase 3+) | +| 2 — Anchors | Right curated files loaded? | 85% per-prompt pass | Every PR (Phase 3+) | +| 3 — Plan quality | Plan correct, complete, concrete, right tier? | mean ≥ 4.0; no dim < 3 | Weekly + pre-release | +| 4 — Cross-LLM | Acceptable on Codex, Gemini? | ≥70% Layer 1 (warn); ≥60% (block at launch) | Weekly | +| 5 — E2E | Sandbox tenant actually works? | 95% rolling 7-day | Nightly (Phase S) | + +## Layout + +``` +evals/ + prompts/.yaml # one prompt set per skill (validated by schema) + schemas/prompt-set.schema.json + golden//.md # golden plans for Layer 3 (Phase 1+) + scorecards/ # rubric for each layer + harness/ + validate_prompts.py # YAML schema validator + run_eval.py # Layer 1 + Layer 2 runner + judge_plans.py # Layer 3 — LLM-as-judge (stub in Phase 0) + cross_llm.py # Layer 4 — multi-LLM (stub in Phase 0) + adapters/ # mock + claude (+ codex, gemini in Phase 1) + tests/ # pytest covering the harness itself + results//.layer{1,2,3}.json +``` + +## Run + +```bash +python3 -m pip install -r evals/harness/requirements.txt +python3 -m evals.harness.validate_prompts +python3 -m evals.harness.run_eval --adapter mock --layer 1 +python3 -m evals.harness.run_eval --adapter mock --layer 2 +python3 -m pytest evals/harness/tests/ -v +``` + +## Authoring checklist (every skill PR from Phase 1+) + +A skill PR is rejected by CI (Phase 3+) unless it includes: + +1. `evals/prompts/.yaml` — ≥10 trigger / ≥5 non-trigger / ≥3 ambiguous prompts; schema-valid. +2. `evals/golden//.md` — ≥3 golden plans for Layer 3. +3. A passing local Layer 1 + Layer 2 run against the Claude adapter. +``` + +- [ ] **Step 2: Run the full Phase 0 smoke check** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills +python3 -m evals.harness.validate_prompts +python3 -m evals.harness.run_eval --adapter mock --layer 1 +python3 -m evals.harness.run_eval --adapter mock --layer 2 +python3 -m pytest evals/harness/tests/ -v +``` + +All four commands must exit 0. + +- [ ] **Step 3: Verify Phase 0 exit criteria from PLAN.md** + +```bash +cd /Users/george.bafaloukas/Dev/tiger-agent-skills + +# 1. Top-level dirs exist +[ -d commands ] && [ -d rules ] && [ -d evals ] && echo "OK: top-level dirs" + +# 2. Six umbrella skill directories exist with the canonical tier set +for s in ping-quickstart ping-foundation ping-orchestration ping-universal-services ping-app-integration ping-identity-for-ai; do + base=plugins/ping-identity/skills/$s + [ -f $base/SKILL.md ] || echo "MISSING: $base/SKILL.md" + [ -f $base/ping-marketplace.json ] || echo "MISSING: $base/ping-marketplace.json" + for tier in curated generated runtime; do + [ -d $base/references/$tier ] || echo "MISSING: $base/references/$tier" + done + [ -f $base/references/runtime/docs-mcp-routing.md ] || echo "MISSING: $base/references/runtime/docs-mcp-routing.md" +done + +# 3. Multi-IDE manifests +[ -f .claude-plugin/marketplace.json ] && [ -f .claude-plugin/plugin.json ] && echo "OK: claude-plugin" +[ -f .cursor-plugin/marketplace.json ] && [ -f .cursor-plugin/plugin.json ] && echo "OK: cursor-plugin" +[ -f plugins/ping-identity/.claude-plugin/plugin.json ] && echo "OK: inner plugin manifest" + +# 4. .well-known and .mdc rule +[ -f .well-known/agent-skills/index.json ] && echo "OK: well-known" +[ -f rules/ping-identity.mdc ] && echo "OK: cursor mdc rule" + +# 5. runtime-selection.md +[ -f rules/runtime-selection.md ] && echo "OK: runtime-selection rule" +``` + +Expected: every relevant line prints `OK: ...` and no `MISSING` line appears. + +- [ ] **Step 4: Commit** + +```bash +git add evals/README.md +git commit -m "docs: evals/README.md and Phase 0 smoke check passes + +Documents the 5-layer eval framework, how to run each layer locally, +and the per-PR authoring checklist (≥10/≥5/≥3 prompts + ≥3 goldens) +that becomes a CI gate in Phase 3. + +Phase 0 smoke check passes: +- validate_prompts: all 6 prompt YAMLs valid against schema +- run_eval --layer 1 --adapter mock: PASS for all 6 skills +- run_eval --layer 2 --adapter mock: PASS +- pytest evals/harness/tests/: 11 passed + +Phase 0 exit criteria from PLAN.md all green. + +Refs: PLAN.md Phase 0 exit criterion" +``` + +--- + +## Phase 0 done — what Phase 1 inherits + +After all 13 tasks land: + +- 6 umbrella skill directories with the canonical `references/{curated,generated,runtime}/` tier set per strategy doc § 6. Three live skills (`ping-quickstart`, `ping-foundation`, `ping-orchestration`) keep all their content; three new skills (`ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`) have empty SKILL.md scaffolds, marketplace metadata, and runtime stubs. +- `rules/runtime-selection.md` is the canonical decision rule for sandbox-vs-production runtime AND for the strategy-doc § 0 tier-discipline rule (curated → generated → docs-mcp). +- A runnable Layer 1 + Layer 2 eval harness with 6 prompt sets and 11 passing pytest cases. Phase 1 wires in the real Claude adapter and the harness becomes a CI gate in Phase 3. +- Multi-IDE manifests in `.claude-plugin/` AND `.cursor-plugin/` (Cloudflare parity), plus the inner `plugins/ping-identity/.claude-plugin/plugin.json` required by strategy doc § 5. +- `rules/ping-identity.mdc` for Cursor parity with Cloudflare's `rules/workers.mdc`. +- `.well-known/agent-skills/index.json` stub ready for Phase 3 to populate. + +The next phase opens with: "Author `ping-universal-services/SKILL.md` body following the routing decision tree per strategy doc § 7, then run `python -m evals.harness.run_eval --adapter claude --layer 1 --skill ping-universal-services` and iterate until ≥90% trigger pass." diff --git a/evals/README.md b/evals/README.md new file mode 100644 index 0000000..ab950f2 --- /dev/null +++ b/evals/README.md @@ -0,0 +1,100 @@ +# Skill Evaluation Harness + +Measures skill accuracy across 5 layers. Layer 1 + Layer 2 are runnable +in Phase 0; Layer 3, 4, 5 are scaffolded and wired up in Phase 1+. + +Strategy doc § 2.7: "Testing and eval frameworks are a progressive +evolution, not a day-1 requirement. We keep the investment low." + +## What gets measured + +| Layer | Question | Bar | When | +|---|---|---|---| +| 1 — Routing | Right umbrella skill activated? | 90% trigger / 90% non-trigger / 80% ambiguous | Every PR (Phase 3+) | +| 2 — Anchors | Right curated files loaded? | 85% per-prompt pass | Every PR (Phase 3+) | +| 3 — Plan quality | Plan correct, complete, concrete, right tier? | mean ≥ 4.0; no dim < 3 | Weekly + pre-release | +| 4 — Cross-LLM | Acceptable on Codex, Gemini? | ≥70% Layer 1 (warn); ≥60% (block at launch) | Weekly | +| 5 — E2E | Sandbox tenant actually works? | 95% rolling 7-day | Nightly (Phase S) | + +See detailed rubrics in `scorecards/`. + +## Layout + +``` +evals/ + prompts/.yaml # one prompt set per skill (validated by schema) + schemas/prompt-set.schema.json + golden//.md # golden plans for Layer 3 (Phase 1+) + scorecards/ + routing-eval.md # Layer 1 rubric + anchor-selection-eval.md # Layer 2 rubric + plan-quality-eval.md # Layer 3 rubric + harness/ + validate_prompts.py # YAML schema validator + run_eval.py # Layer 1 + Layer 2 runner + judge_plans.py # Layer 3 — LLM-as-judge (stub in Phase 0) + cross_llm.py # Layer 4 — multi-LLM driver (stub in Phase 0) + adapters/ + base.py # RunResult dataclass + LLMAdapter Protocol + mock.py # deterministic mock for harness tests + claude.py # Anthropic SDK adapter (stub in Phase 0) + tests/ # pytest covering the harness itself + results//.layer{1,2}.json +``` + +## Quick start + +Install dependencies once: + +```bash +/usr/bin/python3 -m pip install -r evals/harness/requirements.txt +``` + +Validate the prompt sets parse: + +```bash +/usr/bin/python3 -m evals.harness.validate_prompts +``` + +Run Layer 1 against the mock adapter (proves the harness; not a skill quality signal): + +```bash +/usr/bin/python3 -m evals.harness.run_eval --adapter mock --layer 1 +/usr/bin/python3 -m evals.harness.run_eval --adapter mock --layer 2 +``` + +Run harness unit tests: + +```bash +/usr/bin/python3 -m pytest evals/harness/tests/ -v +``` + +## Authoring checklist (every skill PR from Phase 1+) + +A skill PR is rejected by CI (Phase 3+) unless it includes: + +1. `evals/prompts/.yaml` — ≥10 trigger / ≥5 non-trigger / ≥3 ambiguous prompts; schema-valid. +2. `evals/golden//.md` — ≥3 golden plans for Layer 3. +3. A passing local Layer 1 + Layer 2 run against the Claude adapter. + +## Adding a new LLM adapter + +1. Add `evals/harness/adapters/.py` implementing `LLMAdapter`. +2. Wire `_build_adapter()` in `run_eval.py`. +3. Add `` to `LLMS` in `cross_llm.py`. + +## CI wiring (Phase 3+) + +`.github/workflows/run-evals.yml` will run: + +```bash +/usr/bin/python3 -m evals.harness.validate_prompts +/usr/bin/python3 -m evals.harness.run_eval --adapter claude --layer 1 --write-results +/usr/bin/python3 -m evals.harness.run_eval --adapter claude --layer 2 --write-results +``` + +A non-zero exit blocks merge. + +## Why a different judge model for Layer 3 + +Self-judging collapses the eval. The default is cross-model: Claude judges Codex/Gemini output; Codex/Gemini judge Claude output. Override with `--judge `. diff --git a/evals/__init__.py b/evals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/evals/golden/.gitkeep b/evals/golden/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/evals/harness/__init__.py b/evals/harness/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/evals/harness/adapters/__init__.py b/evals/harness/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/evals/harness/adapters/base.py b/evals/harness/adapters/base.py new file mode 100644 index 0000000..6aaec82 --- /dev/null +++ b/evals/harness/adapters/base.py @@ -0,0 +1,18 @@ +"""Adapter base — every LLM driver implements this.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Protocol + + +@dataclass +class RunResult: + loaded_skills: list[str] = field(default_factory=list) + read_paths: list[str] = field(default_factory=list) + final_message: str = "" + clarifying_question: str | None = None + raw_trace: list[dict] = field(default_factory=list) + + +class LLMAdapter(Protocol): + def run(self, prompt: str) -> RunResult: ... diff --git a/evals/harness/adapters/claude.py b/evals/harness/adapters/claude.py new file mode 100644 index 0000000..db5854a --- /dev/null +++ b/evals/harness/adapters/claude.py @@ -0,0 +1,127 @@ +"""Claude (Anthropic) adapter — Layer 1 routing eval implementation. + +How it works: + 1. Build a system prompt that presents all 6 skills described by their + SKILL.md description fields. + 2. Send the user prompt and ask Claude to either route to skill(s) or ask + a clarifying question, returning structured JSON. + 3. Parse the JSON response to populate RunResult. +""" +from __future__ import annotations + +import json +import os +import re +from pathlib import Path + +import anthropic + +from evals.harness.adapters.base import RunResult + +# Bedrock model IDs +_BEDROCK_MODEL = "eu.anthropic.claude-sonnet-4-6" +_DIRECT_MODEL = "claude-sonnet-4-6" + +REPO_ROOT = Path(__file__).resolve().parents[3] +SKILLS_DIR = REPO_ROOT / "plugins" / "ping-identity" / "skills" + +SKILL_NAMES = [ + "ping-quickstart", + "ping-foundation", + "ping-orchestration", + "ping-universal-services", + "ping-app-integration", + "ping-identity-for-ai", +] + + +def _extract_description(skill_name: str) -> str: + skill_file = SKILLS_DIR / skill_name / "SKILL.md" + if not skill_file.exists(): + return f"Skill: {skill_name}" + text = skill_file.read_text() + for line in text.splitlines(): + line = line.strip() + if line.startswith("description:"): + desc = line[len("description:"):].strip().strip('"').strip("'") + if desc: + return desc + return f"Skill: {skill_name}" + + +def _build_system_prompt() -> str: + skill_list = "\n".join( + f"- {name}: {_extract_description(name)}" for name in SKILL_NAMES + ) + return f"""You are a routing assistant for the Ping Identity agent skills system. + +Available skills: +{skill_list} + +Given a user message, respond with a JSON object (no markdown, no prose) in one of two forms: + +Form 1 — route to skill(s): +{{"action": "route", "skills": ["skill-name-1"], "reasoning": "one sentence"}} + +Form 2 — ask a clarifying question: +{{"action": "clarify", "question": "your clarifying question here", "reasoning": "one sentence"}} + +Rules: +- Use "route" when you can confidently identify the relevant skill(s). +- Use "clarify" ONLY when the intent is genuinely ambiguous between two or more skills and a question would resolve it. Do not clarify clear intents. +- You may route to multiple skills if the task genuinely spans them. +- Always output valid JSON and nothing else. +""" + + +class ClaudeAdapter: + def __init__(self) -> None: + if os.environ.get("CLAUDE_CODE_USE_BEDROCK") == "1" or os.environ.get("AWS_BEARER_TOKEN_BEDROCK"): + # Bedrock path — uses AWS_BEARER_TOKEN_BEDROCK + AWS_REGION automatically + self._client = anthropic.AnthropicBedrock( + aws_region=os.environ.get("AWS_REGION", "eu-west-2"), + ) + self._model = _BEDROCK_MODEL + else: + api_key = os.environ.get("ANTHROPIC_API_KEY") + if not api_key: + raise SystemExit("Neither ANTHROPIC_API_KEY nor AWS_BEARER_TOKEN_BEDROCK is set.") + self._client = anthropic.Anthropic(api_key=api_key) + self._model = _DIRECT_MODEL + self._system = _build_system_prompt() + + def run(self, prompt: str) -> RunResult: + message = self._client.messages.create( + model=self._model, + max_tokens=256, + system=self._system, + messages=[{"role": "user", "content": prompt}], + ) + raw = message.content[0].text.strip() + + # Strip accidental markdown code fences + raw = re.sub(r"^```(?:json)?\s*", "", raw) + raw = re.sub(r"\s*```$", "", raw) + + try: + data = json.loads(raw) + except json.JSONDecodeError: + return RunResult( + loaded_skills=[], + final_message=raw, + raw_trace=[{"role": "assistant", "content": raw}], + ) + + if data.get("action") == "clarify": + return RunResult( + loaded_skills=[], + clarifying_question=data.get("question", ""), + final_message=raw, + raw_trace=[{"role": "assistant", "content": raw}], + ) + + return RunResult( + loaded_skills=data.get("skills", []), + final_message=raw, + raw_trace=[{"role": "assistant", "content": raw}], + ) diff --git a/evals/harness/adapters/mock.py b/evals/harness/adapters/mock.py new file mode 100644 index 0000000..fe23040 --- /dev/null +++ b/evals/harness/adapters/mock.py @@ -0,0 +1,32 @@ +"""Deterministic mock adapter for harness self-tests.""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from evals.harness.adapters.base import RunResult + + +@dataclass +class MockRule: + prompt_contains: str + loaded_skills: list[str] = field(default_factory=list) + read_paths: list[str] = field(default_factory=list) + clarifying_question: str | None = None + final_message: str = "[mock plan]" + + +class MockAdapter: + def __init__(self, rules: list[MockRule], default_skills: list[str] | None = None): + self.rules = rules + self.default_skills = default_skills or [] + + def run(self, prompt: str) -> RunResult: + for rule in self.rules: + if rule.prompt_contains.lower() in prompt.lower(): + return RunResult( + loaded_skills=list(rule.loaded_skills), + read_paths=list(rule.read_paths), + final_message=rule.final_message, + clarifying_question=rule.clarifying_question, + ) + return RunResult(loaded_skills=list(self.default_skills)) diff --git a/evals/harness/cross_llm.py b/evals/harness/cross_llm.py new file mode 100644 index 0000000..3e9ba35 --- /dev/null +++ b/evals/harness/cross_llm.py @@ -0,0 +1,30 @@ +"""Layer 4 — Cross-LLM consistency. + +Phase 0 stub: lists what would be run. Phase 1 wires in real adapters +(codex.py, gemini.py) and drives them in parallel. + +Usage: + python3 -m evals.harness.cross_llm --layer 1 +""" +from __future__ import annotations + +import argparse +import sys + +LLMS = ["claude", "codex", "gemini"] + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--layer", type=int, choices=[1, 3], default=1) + args = parser.parse_args() + + print(f"[stub] cross-LLM Layer {args.layer} run plan:") + for llm in LLMS: + print(f" - {llm}: would call run_eval.py / judge_plans.py with --adapter {llm}") + print("\n[stub] Phase 1 wires real adapters. See PLAN.md § Evaluation Layer 4.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/evals/harness/judge_plans.py b/evals/harness/judge_plans.py new file mode 100644 index 0000000..d3afd09 --- /dev/null +++ b/evals/harness/judge_plans.py @@ -0,0 +1,54 @@ +"""Layer 3 — Plan quality eval (LLM-as-judge). + +Phase 0: parses prompt sets and golden plans, prints what it WOULD send +to the judge. Phase 1: wires in actual judge calls. + +Usage: + python3 -m evals.harness.judge_plans --skill ping-orchestration +""" +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" +GOLDEN_DIR = REPO_ROOT / "evals" / "golden" + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--skill", required=True) + parser.add_argument("--judge", default="claude", choices=["claude", "codex", "gemini"]) + args = parser.parse_args() + + pf = PROMPTS_DIR / f"{args.skill}.yaml" + if not pf.exists(): + print(f"no prompt set for {args.skill}", file=sys.stderr) + return 1 + + pset = yaml.safe_load(pf.read_text()) + gdir = GOLDEN_DIR / args.skill + if not gdir.exists(): + print(f"[stub] no goldens at {gdir} — Phase 1 authors them. Skipping.") + return 0 + + for prompt in pset["trigger_prompts"]: + gp = gdir / f"{prompt['id']}.md" + if not gp.exists(): + print(f"[skip] {prompt['id']} — no golden") + continue + print( + f"[would-judge] skill={args.skill} prompt={prompt['id']} " + f"judge={args.judge} golden_chars={len(gp.read_text())}" + ) + + print("\n[stub] Phase 1 wires the judge LLM call. See evals/scorecards/plan-quality-eval.md.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/evals/harness/pyproject.toml b/evals/harness/pyproject.toml new file mode 100644 index 0000000..8432f22 --- /dev/null +++ b/evals/harness/pyproject.toml @@ -0,0 +1,3 @@ +[tool.pytest.ini_options] +testpaths = ["evals/harness/tests"] +pythonpath = ["."] diff --git a/evals/harness/requirements.txt b/evals/harness/requirements.txt new file mode 100644 index 0000000..1a38659 --- /dev/null +++ b/evals/harness/requirements.txt @@ -0,0 +1,4 @@ +pyyaml>=6.0 +jsonschema>=4.0 +pytest>=8.0 +anthropic>=0.40 diff --git a/evals/harness/run_eval.py b/evals/harness/run_eval.py new file mode 100644 index 0000000..922a696 --- /dev/null +++ b/evals/harness/run_eval.py @@ -0,0 +1,241 @@ +"""Layer 1 (routing) + Layer 2 (anchor selection) eval runner. + +Usage: + python3 -m evals.harness.run_eval --adapter mock --layer 1 + python3 -m evals.harness.run_eval --adapter claude --layer 2 --skill ping-orchestration +""" +from __future__ import annotations + +import argparse +import json +import sys +from dataclasses import dataclass, field +from datetime import date +from pathlib import Path + +import yaml + +from evals.harness.adapters.base import LLMAdapter +from evals.harness.adapters.mock import MockAdapter, MockRule + +REPO_ROOT = Path(__file__).resolve().parents[2] +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" +RESULTS_DIR = REPO_ROOT / "evals" / "results" + +LAYER_1_TRIGGER_BAR = 0.90 +LAYER_1_NON_TRIGGER_BAR = 0.90 +LAYER_1_AMBIGUOUS_BAR = 0.80 +LAYER_2_BAR = 0.85 + + +@dataclass +class PromptSet: + skill: str + trigger: list[dict] + non_trigger: list[dict] + ambiguous: list[dict] + + +@dataclass +class Layer1Report: + skill: str + trigger_pass_rate: float + non_trigger_pass_rate: float + ambiguous_pass_rate: float + failures: list[str] = field(default_factory=list) + + @property + def passed_overall(self) -> bool: + return ( + self.trigger_pass_rate >= LAYER_1_TRIGGER_BAR + and self.non_trigger_pass_rate >= LAYER_1_NON_TRIGGER_BAR + and self.ambiguous_pass_rate >= LAYER_1_AMBIGUOUS_BAR + ) + + +@dataclass +class Layer2Report: + skill: str + pass_rate: float + failures: list[str] = field(default_factory=list) + + @property + def passed_overall(self) -> bool: + return self.pass_rate >= LAYER_2_BAR + + +def load_prompt_set(path: Path) -> PromptSet: + with path.open() as f: + data = yaml.safe_load(f) + return PromptSet( + skill=data["skill"], + trigger=data["trigger_prompts"], + non_trigger=data["non_trigger_prompts"], + ambiguous=data["ambiguous_prompts"], + ) + + +def _safe_rate(passed: int, total: int) -> float: + return 1.0 if total == 0 else passed / total + + +def score_layer_1(pset: PromptSet, adapter: LLMAdapter) -> Layer1Report: + failures: list[str] = [] + + t_pass = 0 + for p in pset.trigger: + result = adapter.run(p["prompt"]) + if pset.skill in result.loaded_skills: + t_pass += 1 + else: + failures.append( + f"[trigger] {p['id']} expected {pset.skill}, got {result.loaded_skills}" + ) + + n_pass = 0 + for p in pset.non_trigger: + result = adapter.run(p["prompt"]) + if pset.skill not in result.loaded_skills: + n_pass += 1 + else: + failures.append(f"[non-trigger] {p['id']} {pset.skill} should NOT load") + + a_pass = 0 + for p in pset.ambiguous: + result = adapter.run(p["prompt"]) + cq = (result.clarifying_question or "").lower() + keywords = p["expected_clarification_keywords"] + if cq and any(k.lower() in cq for k in keywords): + a_pass += 1 + else: + failures.append( + f"[ambiguous] {p['id']} expected clarification with one of {keywords}" + ) + + return Layer1Report( + skill=pset.skill, + trigger_pass_rate=_safe_rate(t_pass, len(pset.trigger)), + non_trigger_pass_rate=_safe_rate(n_pass, len(pset.non_trigger)), + ambiguous_pass_rate=_safe_rate(a_pass, len(pset.ambiguous)), + failures=failures, + ) + + +def score_layer_2(pset: PromptSet, adapter: LLMAdapter) -> Layer2Report: + failures: list[str] = [] + counted = 0 + passed = 0 + + for p in pset.trigger: + expected = set(p.get("expected_anchors") or []) + if not expected: + continue + counted += 1 + result = adapter.run(p["prompt"]) + loaded = set(result.read_paths or []) + recall = len(loaded & expected) / len(expected) + precision = (len(loaded & expected) / len(loaded)) if loaded else 0.0 + if recall >= 1.0 and precision >= 0.5: + passed += 1 + else: + failures.append( + f"{p['id']} expected={sorted(expected)} loaded={sorted(loaded)} " + f"recall={recall:.2f} precision={precision:.2f}" + ) + + return Layer2Report( + skill=pset.skill, + pass_rate=_safe_rate(passed, counted), + failures=failures, + ) + + +def _build_adapter(name: str) -> LLMAdapter: + if name == "mock": + rules: list[MockRule] = [] + for prompt_file in sorted(PROMPTS_DIR.glob("*.yaml")): + if prompt_file.stem in {"composition"}: + continue + pset = load_prompt_set(prompt_file) + for p in pset.trigger: + rules.append(MockRule( + prompt_contains=p["prompt"][:30], + loaded_skills=[pset.skill], + read_paths=p.get("expected_anchors") or [], + )) + for p in pset.ambiguous: + rules.append(MockRule( + prompt_contains=p["prompt"][:30], + clarifying_question=" ".join(p["expected_clarification_keywords"]), + )) + return MockAdapter(rules=rules) + if name == "claude": + from evals.harness.adapters.claude import ClaudeAdapter + return ClaudeAdapter() + raise SystemExit(f"unknown adapter: {name}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--adapter", default="mock", choices=["mock", "claude"]) + parser.add_argument("--layer", type=int, choices=[1, 2], default=1) + parser.add_argument("--skill", default=None, help="Run a single skill (default: all)") + parser.add_argument("--write-results", action="store_true") + args = parser.parse_args() + + adapter = _build_adapter(args.adapter) + files = sorted(PROMPTS_DIR.glob("*.yaml")) + if args.skill: + files = [f for f in files if f.stem == args.skill] + + all_pass = True + out: dict[str, dict] = {} + + SKIP_FILES = {"composition"} # multi-skill format, not a per-skill prompt set + + for f in files: + if f.stem in SKIP_FILES: + continue + pset = load_prompt_set(f) + if args.layer == 1: + r = score_layer_1(pset, adapter) + status = "PASS" if r.passed_overall else "FAIL" + print( + f"[L1] {pset.skill} trigger={r.trigger_pass_rate:.0%} " + f"non_trigger={r.non_trigger_pass_rate:.0%} " + f"ambiguous={r.ambiguous_pass_rate:.0%} {status}" + ) + for line in r.failures: + print(f" {line}") + all_pass = all_pass and r.passed_overall + out[pset.skill] = { + "layer": 1, + "trigger": r.trigger_pass_rate, + "non_trigger": r.non_trigger_pass_rate, + "ambiguous": r.ambiguous_pass_rate, + "passed": r.passed_overall, + } + else: + r = score_layer_2(pset, adapter) + status = "PASS" if r.passed_overall else "FAIL" + print(f"[L2] {pset.skill} pass_rate={r.pass_rate:.0%} {status}") + for line in r.failures: + print(f" {line}") + all_pass = all_pass and r.passed_overall + out[pset.skill] = { + "layer": 2, + "pass_rate": r.pass_rate, + "passed": r.passed_overall, + } + + if args.write_results: + day = RESULTS_DIR / date.today().isoformat() + day.mkdir(parents=True, exist_ok=True) + result_file = day / f"{args.adapter}.layer{args.layer}.json" + result_file.write_text(json.dumps(out, indent=2)) + + return 0 if all_pass else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/evals/harness/tests/__init__.py b/evals/harness/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/evals/harness/tests/test_adapters_mock.py b/evals/harness/tests/test_adapters_mock.py new file mode 100644 index 0000000..6afaf18 --- /dev/null +++ b/evals/harness/tests/test_adapters_mock.py @@ -0,0 +1,47 @@ +from evals.harness.adapters.mock import MockAdapter, MockRule + + +def test_mock_adapter_returns_loaded_skills_per_rule(): + rules = [ + MockRule(prompt_contains="MFA", loaded_skills=["ping-quickstart"], read_paths=[]), + MockRule( + prompt_contains="journey", + loaded_skills=["ping-orchestration"], + read_paths=[ + "plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md" + ], + ), + ] + adapter = MockAdapter(rules=rules, default_skills=[]) + + r1 = adapter.run("I want to add MFA to my mobile banking app.") + assert r1.loaded_skills == ["ping-quickstart"] + assert r1.read_paths == [] + assert r1.final_message != "" + + r2 = adapter.run("Build a registration journey with email OTP.") + assert r2.loaded_skills == ["ping-orchestration"] + assert r2.read_paths == [ + "plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md" + ] + + +def test_mock_adapter_default_when_no_rule_matches(): + adapter = MockAdapter(rules=[], default_skills=[]) + r = adapter.run("a prompt that matches nothing at all") + assert r.loaded_skills == [] + assert r.read_paths == [] + + +def test_mock_adapter_supports_clarification_for_ambiguous_prompts(): + rules = [ + MockRule( + prompt_contains="add MFA", + clarifying_question="Are you in PingOne MT, AIC, or on-prem? Workforce or CIAM?", + ), + ] + adapter = MockAdapter(rules=rules, default_skills=[]) + r = adapter.run("I want to add MFA.") + assert r.loaded_skills == [] + assert r.clarifying_question is not None + assert any(k in r.clarifying_question.lower() for k in ["pingone", "aic", "ciam", "workforce"]) diff --git a/evals/harness/tests/test_run_eval.py b/evals/harness/tests/test_run_eval.py new file mode 100644 index 0000000..6b034b2 --- /dev/null +++ b/evals/harness/tests/test_run_eval.py @@ -0,0 +1,99 @@ +from pathlib import Path +import textwrap + +from evals.harness.adapters.mock import MockAdapter, MockRule +from evals.harness.run_eval import score_layer_1, score_layer_2, load_prompt_set + + +def write(tmp_path: Path, name: str, content: str) -> Path: + p = tmp_path / name + p.write_text(textwrap.dedent(content).strip() + "\n") + return p + + +def test_layer1_trigger_correct_when_skill_loaded(tmp_path): + f = write(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "where do I start with Ping?" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: + - id: N-01 + prompt: "build me a journey in AIC" + expected_skill: ping-orchestration + ambiguous_prompts: + - id: A-01 + prompt: "I want MFA please" + expected_clarification_keywords: ["pingone", "aic"] + """) + pset = load_prompt_set(f) + adapter = MockAdapter(rules=[ + MockRule(prompt_contains="where do I start", loaded_skills=["ping-quickstart"]), + MockRule(prompt_contains="build me a journey", loaded_skills=["ping-orchestration"]), + MockRule(prompt_contains="I want MFA", clarifying_question="pingone aic mt or st?"), + ]) + report = score_layer_1(pset, adapter) + assert report.trigger_pass_rate == 1.0 + assert report.non_trigger_pass_rate == 1.0 + assert report.ambiguous_pass_rate == 1.0 + assert report.passed_overall is True + + +def test_layer1_trigger_fails_when_wrong_skill_loaded(tmp_path): + f = write(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "where do I start with Ping" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + pset = load_prompt_set(f) + adapter = MockAdapter(rules=[ + MockRule(prompt_contains="where do I start", loaded_skills=["ping-foundation"]), + ]) + report = score_layer_1(pset, adapter) + assert report.trigger_pass_rate == 0.0 + assert report.passed_overall is False + assert "T-01" in report.failures[0] + + +def test_layer2_recall_and_precision(tmp_path): + anchor = "plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md" + f = write(tmp_path, "ping-orchestration.yaml", """ + skill: ping-orchestration + version: 1 + trigger_prompts: + - id: T-01 + prompt: "build a registration journey in AIC" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + pset = load_prompt_set(f) + + # recall=1.0, precision=1.0 -> pass + adapter_pass = MockAdapter(rules=[MockRule( + prompt_contains="build a registration journey", + loaded_skills=["ping-orchestration"], + read_paths=[anchor], + )]) + rep = score_layer_2(pset, adapter_pass) + assert rep.pass_rate == 1.0 + + # recall=0.0 -> fail + adapter_miss = MockAdapter(rules=[MockRule( + prompt_contains="build a registration journey", + loaded_skills=["ping-orchestration"], + read_paths=["plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md"], + )]) + rep = score_layer_2(pset, adapter_miss) + assert rep.pass_rate == 0.0 diff --git a/evals/harness/tests/test_validate_prompts.py b/evals/harness/tests/test_validate_prompts.py new file mode 100644 index 0000000..f2e7f53 --- /dev/null +++ b/evals/harness/tests/test_validate_prompts.py @@ -0,0 +1,88 @@ +"""Schema validation tests for evals/prompts/*.yaml.""" +import textwrap +from pathlib import Path + +import pytest + +from evals.harness.validate_prompts import ValidationError, validate_prompt_file + + +def write_yaml(tmp_path: Path, name: str, content: str) -> Path: + p = tmp_path / name + p.write_text(textwrap.dedent(content).strip() + "\n") + return p + + +def test_minimal_valid_prompt_set_passes(tmp_path: Path): + f = write_yaml(tmp_path, "ping-quickstart.yaml", """ + skill: ping-quickstart + version: 1 + trigger_prompts: + - id: T-01 + prompt: "Where do I start with Ping Identity?" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: + - id: N-01 + prompt: "Build me a journey in PingOne ST." + expected_skill: ping-orchestration + ambiguous_prompts: + - id: A-01 + prompt: "I want to add MFA." + expected_clarification_keywords: ["pingone", "aic"] + """) + validate_prompt_file(f) # no exception = pass + + +def test_skill_field_must_match_filename(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-orchestration + version: 1 + trigger_prompts: [] + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="skill .* must match filename"): + validate_prompt_file(f) + + +def test_missing_required_top_level_field_fails(tmp_path: Path): + f = write_yaml(tmp_path, "ping-orchestration.yaml", """ + skill: ping-orchestration + trigger_prompts: [] + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="version"): + validate_prompt_file(f) + + +def test_trigger_prompt_requires_id_and_prompt(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-foundation + version: 1 + trigger_prompts: + - prompt: "missing id" + expected_anchors: [] + expected_tier: curated + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="id"): + validate_prompt_file(f) + + +def test_expected_tier_enum_enforced(tmp_path: Path): + f = write_yaml(tmp_path, "ping-foundation.yaml", """ + skill: ping-foundation + version: 1 + trigger_prompts: + - id: T-01 + prompt: "test prompt for tier validation" + expected_anchors: [] + expected_tier: invalid-value + non_trigger_prompts: [] + ambiguous_prompts: [] + """) + with pytest.raises(ValidationError, match="expected_tier"): + validate_prompt_file(f) diff --git a/evals/harness/validate_prompts.py b/evals/harness/validate_prompts.py new file mode 100644 index 0000000..d3da8e7 --- /dev/null +++ b/evals/harness/validate_prompts.py @@ -0,0 +1,73 @@ +"""Validate evals/prompts/*.yaml files against the prompt-set JSON schema.""" +from __future__ import annotations + +import json +import sys +from pathlib import Path +from typing import Iterable + +import yaml +from jsonschema import Draft7Validator + +REPO_ROOT = Path(__file__).resolve().parents[2] +SCHEMA_PATH = REPO_ROOT / "evals" / "schemas" / "prompt-set.schema.json" +PROMPTS_DIR = REPO_ROOT / "evals" / "prompts" + + +class ValidationError(Exception): + pass + + +def _load_schema() -> dict: + with SCHEMA_PATH.open() as f: + return json.load(f) + + +def validate_prompt_file(path: Path) -> None: + with path.open() as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise ValidationError(f"{path}: top-level must be a mapping") + + schema = _load_schema() + errors = sorted(Draft7Validator(schema).iter_errors(data), key=lambda e: e.path) + if errors: + msgs = "; ".join( + f"{'/'.join(map(str, e.path)) or ''}: {e.message}" for e in errors + ) + raise ValidationError(f"{path}: {msgs}") + + expected_skill = path.stem + if data["skill"] != expected_skill: + raise ValidationError( + f"{path}: skill '{data['skill']}' must match filename '{expected_skill}'" + ) + + +SKIP_VALIDATION = {"composition"} # composition.yaml uses a different schema (multi-skill) + + +def validate_all(paths: Iterable[Path] | None = None) -> list[Path]: + paths = list(paths) if paths is not None else sorted(PROMPTS_DIR.glob("*.yaml")) + failed: list[Path] = [] + for p in paths: + if p.stem in SKIP_VALIDATION: + print(f"SKIP {p.relative_to(REPO_ROOT)} (composition schema)") + continue + try: + validate_prompt_file(p) + print(f"OK {p.relative_to(REPO_ROOT)}") + except ValidationError as exc: + failed.append(p) + print(f"FAIL {exc}", file=sys.stderr) + return failed + + +def main() -> int: + failed = validate_all() + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/evals/prompts/composition.yaml b/evals/prompts/composition.yaml new file mode 100644 index 0000000..e6d80c1 --- /dev/null +++ b/evals/prompts/composition.yaml @@ -0,0 +1,42 @@ +skill: composition +version: 1 +composition_prompts: + - id: C-01 + prompt: "I want to add KYC during registration on my React app" + expected_skills: + - ping-quickstart + - ping-orchestration + - ping-universal-services + - ping-app-integration + notes: "Entry via ping-quickstart (platform/task unknown). KYC = ping-universal-services (PingOne Verify). Registration journey = ping-orchestration. React wiring = ping-app-integration." + + - id: C-02 + prompt: "Add MFA and risk-based step-up to our iOS banking app" + expected_skills: + - ping-orchestration + - ping-universal-services + - ping-app-integration + notes: "MFA and risk-based step-up = ping-orchestration (journey/flow design) + ping-universal-services (PingOne Protect for risk scoring). iOS SDK wiring = ping-app-integration." + + - id: C-03 + prompt: "Build an AI agent that can reset employee passwords with proper audit" + expected_skills: + - ping-identity-for-ai + - ping-foundation + - ping-orchestration + notes: "AI agent identity patterns = ping-identity-for-ai (workforce helpdesk AI). Directory/policy setup = ping-foundation. Self-service password reset flow = ping-orchestration." + + - id: C-04 + prompt: "Set up PingOne MT, build a registration journey in DaVinci with email verification, and wire it into our Android app" + expected_skills: + - ping-foundation + - ping-orchestration + - ping-app-integration + notes: "Tenant/app registration = ping-foundation (PingOne MT). DaVinci flow with email verification = ping-orchestration. Android SDK integration = ping-app-integration." + + - id: C-05 + prompt: "Secure an AI chatbot that calls our customer data API with Verified Trust and logs every action" + expected_skills: + - ping-identity-for-ai + - ping-universal-services + notes: "AI chatbot identity security and Verified Trust = ping-identity-for-ai. API authorization, audit, and service invocation = ping-universal-services (Authorize + cross-platform service patterns)." diff --git a/evals/prompts/ping-app-integration.yaml b/evals/prompts/ping-app-integration.yaml new file mode 100644 index 0000000..45f58bd --- /dev/null +++ b/evals/prompts/ping-app-integration.yaml @@ -0,0 +1,153 @@ +skill: ping-app-integration +version: 2 +trigger_prompts: + - id: T-01 + prompt: "Integrate the Ping orchestration SDK with my Android app to drive AIC journeys." + expected_anchors: + - "references/curated/app-integration-overview.md" + - "references/curated/mobile-integration-basics.md" + expected_tier: curated + notes: "Android + AIC Journey — primary mobile integration case." + + - id: T-02 + prompt: "Use the Ping iOS SDK in my Swift app to connect to DaVinci flows." + expected_anchors: + - "references/curated/mobile-integration-basics.md" + expected_tier: curated + notes: "iOS Swift + DaVinci — primary iOS integration case." + + - id: T-03 + prompt: "Wire OIDC login into my React web application using PingOne MT as the identity provider." + expected_anchors: + - "references/curated/web-integration-basics.md" + expected_tier: curated + notes: "React + PingOne MT OIDC — primary web integration case." + + - id: T-04 + prompt: "How do I add the Ping Android SDK to my Kotlin app and authenticate against an AIC Journey?" + expected_anchors: + - "references/curated/mobile-integration-basics.md" + expected_tier: curated + notes: "Android SDK + AIC Journey — variation on T-01 emphasizing SDK setup and init." + + - id: T-05 + prompt: "I want to build a JavaScript SPA that uses @forgerock/davinci-client to drive a PingOne DaVinci flow." + expected_anchors: + - "references/curated/web-integration-basics.md" + expected_tier: curated + notes: "JS SPA + DaVinci client — additional React/JS web integration prompt." + + - id: T-06 + prompt: "My web app needs to redirect users to the Ping-hosted login page and receive tokens after they authenticate." + expected_anchors: + - "references/curated/web-integration-basics.md" + expected_tier: curated + notes: "Browser-based hosted login redirect — covers the hosted login page + auth code callback pattern." + + - id: T-07 + prompt: "How do I configure my Java web application to act as a SAML SP against PingFederate?" + expected_anchors: + - "references/curated/web-integration-basics.md" + expected_tier: curated + notes: "On-prem SAML SP integration with PingFederate — covers SAML section of web integration." + + - id: T-08 + prompt: "We are migrating our Android app from the ForgeRock SDK to the new Ping SDK. What are the breaking changes?" + expected_anchors: + - "references/curated/integration-troubleshooting-basics.md" + - "references/curated/mobile-integration-basics.md" + expected_tier: curated + notes: "ForgeRock to Ping SDK migration — Android; covers renamed packages and init API changes." + + - id: T-09 + prompt: "My app is getting a redirect_uri_mismatch error during the OIDC login flow. How do I diagnose and fix it?" + expected_anchors: + - "references/curated/integration-troubleshooting-basics.md" + expected_tier: curated + notes: "Redirect URI mismatch — most common integration failure; explicit troubleshooting prompt." + + - id: T-10 + prompt: "The token exchange is failing with a CORS error even though the authorization redirect worked fine. What is causing this?" + expected_anchors: + - "references/curated/integration-troubleshooting-basics.md" + expected_tier: curated + notes: "CORS on token endpoint — failure mode 2; covers diagnosis and platform-specific fixes." + + - id: T-11 + prompt: "After my app goes to the background, the token refresh stops working and users are logged out. How do I fix this?" + expected_anchors: + - "references/curated/integration-troubleshooting-basics.md" + expected_tier: curated + notes: "Token refresh failure after background — covers offline_access scope and token storage accessibility." + + # ----- Strategy doc § 9 use cases (Getting Started 0–30) ----- + + - id: T-50 + prompt: "Integrate my existing React web app with PingOne — give me the implementation path for OIDC sign-in." + expected_anchors: + - plugins/ping-identity/skills/ping-app-integration/references/curated/web-integration-basics.md + expected_tier: curated + notes: "strategy-uc-6 (web)" + + - id: T-51 + prompt: "Connect my Android app to a DaVinci flow on PingOne MT — install SDK, init, handle callbacks." + expected_anchors: + - plugins/ping-identity/skills/ping-app-integration/references/curated/mobile-integration-basics.md + expected_tier: curated + notes: "strategy-uc-6 (mobile Android)" + + - id: T-52 + prompt: "Integrate my Node.js backend with PingOne using Authorization Code + PKCE — including token validation, refresh, and 429 retry handling." + expected_anchors: + - plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md + expected_tier: curated + notes: "strategy-uc-6 (server-side)" + + - id: T-53 + prompt: "I need a service-to-service token exchange between two backend microservices using Ping — what's the right pattern?" + expected_anchors: + - plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md + expected_tier: curated + notes: "strategy-uc-6 (M2M / token exchange)" + +non_trigger_prompts: + - id: N-01 + prompt: "Register an OIDC application in my PingOne MT admin console." + expected_skill: ping-foundation + notes: "Platform-side app registration — no code; admin console task belongs to ping-foundation." + + - id: N-02 + prompt: "Build a DaVinci flow for user registration with email OTP." + expected_skill: ping-orchestration + notes: "Flow design task — belongs to ping-orchestration, not app-side implementation." + + - id: N-03 + prompt: "How do I configure a sign-on policy in PingOne Advanced Identity Cloud?" + expected_skill: ping-foundation + notes: "Admin-side policy configuration — no SDK code; platform setup belongs to ping-foundation." + + - id: N-04 + prompt: "Design a Journey tree in PingAM that handles adaptive MFA based on device risk score." + expected_skill: ping-orchestration + notes: "Journey node graph design — flow design task for ping-orchestration, not app integration." + + - id: N-05 + prompt: "How do I invoke the PingOne Verify service to verify a user's identity document from my backend?" + expected_skill: ping-universal-services + notes: "Universal service invocation (Verify) — service API call, not app SDK integration; belongs to ping-universal-services." + +ambiguous_prompts: + - id: A-01 + prompt: "I need to integrate Ping into my app." + expected_clarification_keywords: ["android", "ios", "react", "web", "mobile", "sdk", "platform", "oidc"] + notes: "Underspecified — could be mobile or web, any platform; needs clarification on app type and platform." + + - id: A-02 + prompt: "I need to add login to my app." + expected_clarification_keywords: ["android", "ios", "web", "react", "mobile", "platform", "journey", "davinci", "oidc"] + notes: "Extremely underspecified — could be mobile, web, any platform, any flow type; needs clarification on all dimensions." + + - id: A-03 + prompt: "How do I integrate Ping with my backend?" + expected_clarification_keywords: ["app-integration", "foundation", "api", "oidc", "client-credentials", "token", "sdk", "server"] + notes: "Ambiguous between app-integration (token validation, SDK) and ping-foundation (app registration, API config); backend could mean resource server or admin API." diff --git a/evals/prompts/ping-foundation.yaml b/evals/prompts/ping-foundation.yaml new file mode 100644 index 0000000..95b9ce6 --- /dev/null +++ b/evals/prompts/ping-foundation.yaml @@ -0,0 +1,145 @@ +skill: ping-foundation +version: 1 +trigger_prompts: + - id: T-01 + prompt: "How do I provision a new PingOne MT environment and set up my first OIDC application?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md + expected_tier: curated + + - id: T-02 + prompt: "How do I register an OIDC application in AIC for my React app?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md + expected_tier: curated + + - id: T-03 + prompt: "How do I set up a PingOne ST tenant from scratch — realms, managed objects, admin setup?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md + expected_tier: curated + + - id: T-04 + prompt: "Configure an SAML SP connection on PingFederate for Salesforce SSO." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md + expected_tier: curated + + - id: T-05 + prompt: "How do I connect an external LDAP directory to PingOne ST?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md + expected_tier: curated + + - id: T-06 + prompt: "Set up custom branding and themes in AIC for our consumer portal." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md + expected_tier: curated + + - id: T-07 + prompt: "Configure an authentication policy in PingOne ST for step-up MFA." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md + expected_tier: curated + + - id: T-08 + prompt: "How do I install and initially configure PingDirectory on Linux?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md + expected_tier: curated + + - id: T-09 + prompt: "How do I set up MFA policies in PingOne MT for my workforce users?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md + expected_tier: curated + + - id: T-10 + prompt: "Set up an OIDC app in PingOne MT with client credentials grant for a backend service." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md + expected_tier: curated + + # ----- Strategy doc § 9 use cases (Getting Started 0–30) ----- + + - id: T-50 + prompt: "Set up the right foundation for my PingOne MT environment — we need tenants, admins, populations, and baseline policies." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md + expected_tier: curated + notes: "strategy-uc-2" + + - id: T-51 + prompt: "Set up the AIC foundation for our customer-facing realm — tenant, identity store, journeys, and themes." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md + expected_tier: curated + notes: "strategy-uc-2 (AIC)" + + - id: T-52 + prompt: "Create the OIDC client configuration I need so my mobile app can integrate with PingOne MT." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/app-registration.md + expected_tier: curated + notes: "strategy-uc-3 (PingOne MT)" + + - id: T-53 + prompt: "I need to register a SAML application in PingOne ST for our enterprise app integration." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md + expected_tier: curated + notes: "strategy-uc-3 (AIC SAML)" + + - id: T-54 + prompt: "Match the Ping UX to my application brand on PingOne MT — custom domain, logo, email templates, DaVinci flow pages." + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md + expected_tier: curated + notes: "strategy-uc-8 (PingOne MT)" + + - id: T-55 + prompt: "How do I theme our AIC hosted login pages to match our consumer brand?" + expected_anchors: + - plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md + expected_tier: curated + notes: "strategy-uc-8 (AIC)" + +non_trigger_prompts: + - id: N-01 + prompt: "Build a DaVinci flow for customer login with risk-based step-up MFA." + expected_skill: ping-orchestration + notes: "Flow design — not admin setup." + + - id: N-02 + prompt: "Where do I start with Ping Identity? We don't know which product to pick." + expected_skill: ping-quickstart + + - id: N-03 + prompt: "Integrate the Ping Android SDK with my DaVinci flows." + expected_skill: ping-app-integration + + - id: N-04 + prompt: "Add PingOne Verify KYC to my registration flow." + expected_skill: ping-orchestration + notes: "Service invocation inside a flow — not admin setup." + + - id: N-05 + prompt: "Give my AI agent a verified identity using Ping." + expected_skill: ping-identity-for-ai + +ambiguous_prompts: + - id: A-01 + prompt: "Configure MFA." + expected_clarification_keywords: ["pingone", "aic", "platform", "mt", "st", "policy", "journey"] + notes: "MFA config (foundation) vs MFA step-up in a journey (orchestration)." + + - id: A-02 + prompt: "Set up SSO." + expected_clarification_keywords: ["pingone", "aic", "software", "platform", "workforce", "ciam"] + + - id: A-03 + prompt: "Add a user to Ping." + expected_clarification_keywords: ["pingone", "aic", "directory", "platform", "mt", "st"] diff --git a/evals/prompts/ping-identity-for-ai.yaml b/evals/prompts/ping-identity-for-ai.yaml new file mode 100644 index 0000000..45f0417 --- /dev/null +++ b/evals/prompts/ping-identity-for-ai.yaml @@ -0,0 +1,119 @@ +skill: ping-identity-for-ai +version: 1 +trigger_prompts: + - id: T-01 + prompt: "Give my AI agent a verified identity so it can securely access our internal APIs using Ping." + expected_anchors: + - references/curated/agent-security-patterns.md + expected_tier: curated + notes: "Primary anchor is agent-security-patterns (M2M auth, client credentials)." + + - id: T-02 + prompt: "How do I apply Verified Trust signals in my MCP server to authenticate requests from AI agents?" + expected_anchors: + - references/curated/verified-trust-overview.md + expected_tier: curated + + - id: T-03 + prompt: "Design a workforce helpdesk AI authentication pattern using Ping Identity for our internal support bot." + expected_anchors: + - references/curated/workforce-helpdesk-ai.md + expected_tier: curated + + - id: T-04 + prompt: "My AI agent needs to call PingOne APIs using the client credentials flow. How do I set that up?" + expected_anchors: + - references/curated/agent-security-patterns.md + expected_tier: curated + notes: "Client credentials for AI agent — direct hit on agent-security-patterns." + + - id: T-05 + prompt: "What scopes should I assign to my autonomous AI agent's OAuth client registration? I want minimal privilege." + expected_anchors: + - references/curated/agent-security-patterns.md + expected_tier: curated + notes: "Token scoping strategy — direct hit on agent-security-patterns Pattern 2." + + - id: T-06 + prompt: "How do I issue a delegated token so our helpdesk AI bot can reset passwords on behalf of authenticated employees?" + expected_anchors: + - references/curated/workforce-helpdesk-ai.md + expected_tier: curated + notes: "Delegated token for workforce helpdesk AI." + + - id: T-07 + prompt: "We want to issue a verifiable credential to an AI agent that includes the outcome of PingOne Verify identity proofing." + expected_anchors: + - references/curated/verified-trust-overview.md + expected_tier: curated + notes: "Verified Trust with identity proofing outcome as VC input." + + - id: T-08 + prompt: "One of our AI agents has been compromised. How do I revoke its access immediately and ensure our audit logs capture what it did?" + expected_anchors: + - references/curated/agent-security-patterns.md + expected_tier: curated + notes: "Revocation and audit trail for AI agents." + + - id: T-09 + prompt: "We are building an LLM-fronted customer portal. How do we authenticate end users of this AI-powered application using PingOne?" + expected_anchors: + - references/curated/identity-for-ai-overview.md + - references/curated/workforce-helpdesk-ai.md + expected_tier: curated + notes: "AI app authentication for an LLM-fronted application." + + - id: T-10 + prompt: "What is the recommended identity onboarding process for registering a new AI agent with Ping Identity so it can start calling protected APIs?" + expected_anchors: + - references/curated/identity-for-ai-overview.md + - references/curated/agent-security-patterns.md + expected_tier: curated + notes: "Identity proofing / onboarding for AI agent — covers both overview and agent security." + +non_trigger_prompts: + - id: N-01 + prompt: "Set up PingOne MT and register an OIDC application for my web portal." + expected_skill: ping-foundation + notes: "Standard app registration — no AI context. Belongs in ping-foundation." + + - id: N-02 + prompt: "Build a user registration journey in AIC with email OTP and progressive profiling." + expected_skill: ping-orchestration + notes: "Journey design for human users — no AI context. Belongs in ping-orchestration." + + - id: N-03 + prompt: "How do I integrate the PingOne SDK into my React Native app so users can log in with their PingOne credentials?" + expected_skill: ping-app-integration + notes: "React SDK integration for human users — no AI context. Belongs in ping-app-integration." + + - id: N-04 + prompt: "Configure a DaVinci flow that adds MFA to the login journey for our B2C customer portal." + expected_skill: ping-orchestration + notes: "DaVinci flow for user MFA — not AI-specific. Belongs in ping-orchestration." + + - id: N-05 + prompt: "What is the difference between PingOne MT and PingOne ST and which one should I choose for my new project?" + expected_skill: ping-quickstart + notes: "Platform selection and orientation. Belongs in ping-quickstart." + + - id: N-06 + prompt: "Configure a PingOne Protect risk policy and set a risk threshold for adaptive authentication on our login journey." + expected_skill: ping-universal-services + notes: "PingOne Protect as a standalone risk service — no AI agent context. Belongs in ping-universal-services, not ping-identity-for-ai." + +ambiguous_prompts: + - id: A-01 + prompt: "I need to authenticate an agent." + expected_clarification_keywords: ["ai", "human", "user", "machine", "api", "bot", "verified trust", "oidc"] + notes: "Agent could mean an AI agent (identity-for-ai) or a Ping agent/service (foundation)." + + - id: A-02 + prompt: "I need to secure API access for an automated process that runs on a schedule and calls our internal services." + expected_clarification_keywords: ["ai", "agent", "batch", "machine", "llm", "autonomous", "client credentials", "job"] + notes: "Could be an AI agent (identity-for-ai / agent-security-patterns) or a traditional batch job (foundation / standard OAuth). Clarify whether the process is AI-driven or a scripted automation." + + - id: A-03 + prompt: "How do I add trust to my application so that downstream services can rely on the claims it presents?" + expected_clarification_keywords: ["verified trust", "oidc", "jwt", "signed assertion", "ai", "verifiable credential", "standard oidc"] + notes: "Could be Verified Trust / verifiable credentials (identity-for-ai) or standard OIDC token-based trust (foundation). Clarify whether the application is an AI agent presenting verifiable credentials or a standard app consuming OIDC tokens." diff --git a/evals/prompts/ping-orchestration.yaml b/evals/prompts/ping-orchestration.yaml new file mode 100644 index 0000000..3b4e5fa --- /dev/null +++ b/evals/prompts/ping-orchestration.yaml @@ -0,0 +1,147 @@ +skill: ping-orchestration +version: 1 +trigger_prompts: + - id: T-01 + prompt: "I want to build a registration journey in PingOne ST that collects email, sends a verification OTP, and creates a managed object on success. What nodes do I need and in what order?" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/basic-auth-nodes.md + expected_tier: curated + + - id: T-02 + prompt: "Build a DaVinci flow that does username/password login, checks risk with PingOne Protect, and triggers MFA step-up if risk is medium or high." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md + expected_tier: curated + + - id: T-03 + prompt: "How do I add MFA nodes to an existing AIC authentication journey? I want to support both TOTP and push notification." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md + expected_tier: curated + + - id: T-04 + prompt: "Design an AIC passwordless registration journey that collects a WebAuthn credential." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md + expected_tier: curated + + - id: T-05 + prompt: "I need a social login plus local registration flow in PingOne ST. How should I structure the journey?" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/social-and-local-registration-authentication.md + expected_tier: curated + + - id: T-06 + prompt: "Build a self-service password reset flow in AIC using email OTP." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/password-reset-and-update.md + expected_tier: curated + + - id: T-07 + prompt: "How do I add a scripted decision node to my PingOne ST authentication tree to check a custom attribute?" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/utility-nodes.md + expected_tier: curated + notes: "Scripted Decision node documentation lives in utility-nodes.md (scripting section). No separate scripted-decision-nodes.md exists." + + - id: T-08 + prompt: "I'm building a financial services app and need to add PingOne Protect risk integration to my AIC journey to trigger step-up for high-risk transactions." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/financial-services-step-up.md + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/risk-management-nodes.md + expected_tier: curated + + - id: T-09 + prompt: "How does the federation node work in PingOne ST for SAML IdP delegation?" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/federation-contextual-nodes.md + expected_tier: curated + + - id: T-10 + prompt: "Design a progressive profiling journey in AIC that collects additional user attributes after initial login." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/progressive-profiling.md + expected_tier: curated + + # ----- Strategy doc § 9 use cases (Getting Started 0–30) ----- + + - id: T-50 + prompt: "Build me a secure sign-up, sign-in, and recovery experience in AIC — registration, login, password reset, account recovery." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md + expected_tier: curated + notes: "strategy-uc-4 (AIC)" + + - id: T-51 + prompt: "Design a DaVinci registration flow with email verification and inline MFA enrollment for PingOne MT." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-registration-and-mfa.md + expected_tier: curated + notes: "strategy-uc-4 (DaVinci)" + + - id: T-52 + prompt: "Add passkey / FIDO2 passwordless authentication to our customer journey, with magic link fallback." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/cross-platform/passkeys-and-passwordless.md + expected_tier: curated + notes: "strategy-uc-5 (passkeys)" + + - id: T-53 + prompt: "Build or adapt the orchestration layer for our recovery flow — the existing journey isn't handling MFA reset correctly." + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/account-recovery-and-username-reminder.md + expected_tier: curated + notes: "strategy-uc-7 (recovery)" + + - id: T-54 + prompt: "How do I add risk-based step-up MFA to our DaVinci sign-in flow using PingOne Protect signals?" + expected_anchors: + - plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md + expected_tier: curated + notes: "strategy-uc-5 (Protect step-up)" + +non_trigger_prompts: + - id: N-01 + prompt: "How do I install PingFederate on RHEL and configure initial admin settings?" + expected_skill: ping-foundation + notes: "On-prem admin setup — not flow design." + + - id: N-02 + prompt: "Where do I start with Ping Identity? We don't know which platform to use." + expected_skill: ping-quickstart + notes: "Platform selection and orientation — not flow design." + + - id: N-03 + prompt: "How do I register an OIDC app in my PingOne MT environment?" + expected_skill: ping-foundation + notes: "App registration is a platform configuration task, not orchestration." + + - id: N-04 + prompt: "Integrate the Ping iOS SDK with my AIC journeys in my Swift app." + expected_skill: ping-app-integration + notes: "SDK integration into an app is handled by ping-app-integration, not flow design." + + - id: N-05 + prompt: "Give my AI agent a Verified Trust identity for accessing our APIs." + expected_skill: ping-identity-for-ai + notes: "AI agent identity and trust — not authentication flow design." + + - id: N-06 + prompt: "How do I configure PingOne Verify for document proofing without embedding it in a journey?" + expected_skill: ping-universal-services + notes: "Standalone universal service invocation without flow design — should route to ping-universal-services per the skill's When NOT to use section." + +ambiguous_prompts: + - id: A-01 + prompt: "Should I use a journey or a DaVinci flow?" + expected_clarification_keywords: ["pingone", "aic", "mt", "st", "platform", "product"] + + - id: A-02 + prompt: "AIC or DaVinci for my login flow?" + expected_clarification_keywords: ["use case", "workforce", "ciam", "requirement", "platform"] + + - id: A-03 + prompt: "Where do I configure MFA in Ping?" + expected_clarification_keywords: ["pingone", "aic", "platform", "mt", "st", "journey", "policy"] diff --git a/evals/prompts/ping-quickstart.yaml b/evals/prompts/ping-quickstart.yaml new file mode 100644 index 0000000..3a4333c --- /dev/null +++ b/evals/prompts/ping-quickstart.yaml @@ -0,0 +1,130 @@ +skill: ping-quickstart +version: 1 +trigger_prompts: + - id: T-01 + prompt: "Where do I start with Ping Identity? We're a new company and don't know which product to pick." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + expected_tier: curated + notes: "Classic orientation prompt — platform unknown." + + - id: T-02 + prompt: "We're evaluating PingOne vs PingOne Advanced Identity Cloud vs PingFederate. What questions should we be asking?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + expected_tier: curated + + - id: T-03 + prompt: "I inherited a Ping deployment and don't know where to begin. How do I get oriented?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + expected_tier: curated + + - id: T-04 + prompt: "Should we go with PingOne cloud or PingFederate on-prem for our enterprise SSO project?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + expected_tier: curated + + - id: T-05 + prompt: "We're a bank building a new mobile banking app and need login plus MFA. Where do we start with Ping?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/common-starting-patterns.md + expected_tier: curated + notes: "CIAM mobile — platform unknown." + + - id: T-06 + prompt: "We're migrating from ForgeRock to Ping. What does the migration path look like?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/common-starting-patterns.md + expected_tier: curated + + - id: T-07 + prompt: "Help me understand the Ping Identity product landscape before we commit to a platform." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + expected_tier: curated + + - id: T-08 + prompt: "We need CIAM for our customer portal. We're not sure whether to use PingOne or AIC." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/common-starting-patterns.md + expected_tier: curated + + - id: T-09 + prompt: "We want to add identity verification KYC to our onboarding flow. Where do we start?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/common-starting-patterns.md + expected_tier: curated + + - id: T-10 + prompt: "We're setting up Ping for employee identity for the first time. What's the recommended starting point?" + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/common-starting-patterns.md + expected_tier: curated + + # ----- Strategy doc § 9 use cases (Getting Started 0–30) ----- + + - id: T-50 + prompt: "Help me get started with Ping for my use case — we want a customer login experience but don't know which Ping product fits." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/getting-started-overview.md + - plugins/ping-identity/skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md + expected_tier: curated + notes: "strategy-uc-1" + + - id: T-51 + prompt: "Test this end to end and tell me what I need to change before going live with our Ping setup." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/end-to-end-validation.md + expected_tier: curated + notes: "strategy-uc-9 — cross-cutting validation" + + - id: T-52 + prompt: "How do I validate that my Ping configuration actually works? I just finished building a sign-on policy and a journey." + expected_anchors: + - plugins/ping-identity/skills/ping-quickstart/references/curated/end-to-end-validation.md + expected_tier: curated + notes: "strategy-uc-9" + +non_trigger_prompts: + - id: N-01 + prompt: "Build me a registration journey in PingOne ST that collects email and sends an OTP." + expected_skill: ping-orchestration + notes: "Platform and task named explicitly — route to orchestration." + + - id: N-02 + prompt: "Add an OIDC application to my AIC tenant." + expected_skill: ping-foundation + notes: "Specific admin task on a named platform." + + - id: N-03 + prompt: "Integrate the Ping iOS SDK with our AIC journeys." + expected_skill: ping-app-integration + + - id: N-04 + prompt: "Score risk with PingOne Protect during my login flow and adapt the journey." + expected_skill: ping-orchestration + notes: "Specific orchestration + services task — not an orientation question." + + - id: N-05 + prompt: "Give my AI agent a verified identity for API access using Ping." + expected_skill: ping-identity-for-ai + +ambiguous_prompts: + - id: A-01 + prompt: "I want to add MFA." + expected_clarification_keywords: ["pingone", "aic", "platform", "workforce", "ciam", "mt", "st"] + + - id: A-02 + prompt: "How do I do passwordless?" + expected_clarification_keywords: ["pingone", "aic", "platform", "product", "davinci", "journey"] + + - id: A-03 + prompt: "We need risk-based authentication." + expected_clarification_keywords: ["pingone", "aic", "platform", "protect", "davinci", "journey"] diff --git a/evals/prompts/ping-universal-services.yaml b/evals/prompts/ping-universal-services.yaml new file mode 100644 index 0000000..e727c5d --- /dev/null +++ b/evals/prompts/ping-universal-services.yaml @@ -0,0 +1,176 @@ +skill: ping-universal-services +version: 2 +trigger_prompts: + - id: T-01 + prompt: "Add PingOne Verify for KYC identity proofing during my customer registration journey in AIC." + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "PingOne ST / AIC context — Verify for KYC." + + - id: T-02 + prompt: "Score risk with PingOne Protect and adapt the authentication journey based on the risk signal." + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "Protect risk evaluation — platform-ambiguous." + + - id: T-03 + prompt: "Issue a verifiable credential to a user who completed identity proofing in my PingOne environment." + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "PingOne Credentials issuance after Verify." + + - id: T-04 + prompt: "Add PingOne Protect risk evaluation to my DaVinci login flow and branch on LOW, MEDIUM, HIGH scores." + expected_anchors: + - references/curated/service-invocation-patterns.md + - references/curated/cross-platform-service-usage.md + expected_tier: curated + notes: "PingOne MT (DaVinci) Protect invocation — explicit platform context." + + - id: T-05 + prompt: "I'm using PingOne Advanced Identity Cloud. How do I add the PingOne Protect evaluation node to my login journey and handle the Challenge and Deny outcomes?" + expected_anchors: + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "PingOne ST / AIC Protect journey node." + + - id: T-06 + prompt: "My users need to complete a document + liveness check (KYC) before they can access their account in my PingOne MT DaVinci flow. How do I add PingOne Verify?" + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "PingOne MT (DaVinci) Verify KYC." + + - id: T-07 + prompt: "How do I issue a PingOne verifiable credential to a user after they complete onboarding?" + expected_anchors: + - references/curated/service-invocation-patterns.md + expected_tier: curated + notes: "PingOne Credentials issuance." + + - id: T-08 + prompt: "Set up PingOne IGA so managers can certify access to sensitive applications on a quarterly basis." + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/universal-services-overview.md + expected_tier: curated + notes: "PingOne IGA governance / access review." + + - id: T-09 + prompt: "I want to enforce fine-grained attribute-based access control at my API gateway using PingOne Authorize — how do I connect it to my PingFederate deployment?" + expected_anchors: + - references/curated/service-invocation-patterns.md + - references/curated/cross-platform-service-usage.md + expected_tier: curated + notes: "PingOne Authorize with PingFederate as enforcement point." + + - id: T-10 + prompt: "I'm not sure which Ping shared service I need — I want to check whether a user is who they say they are during a high-value transaction. Is that Verify, Protect, or MFA?" + expected_anchors: + - references/curated/choosing-the-right-service.md + - references/curated/universal-services-overview.md + expected_tier: curated + notes: "Cross-service selection — which service do I need." + + - id: T-11 + prompt: "How do I invoke the PingOne IGA connector from an AIC journey node to check entitlements during login?" + expected_anchors: + - references/curated/service-invocation-patterns.md + - references/curated/cross-platform-service-usage.md + expected_tier: curated + notes: "PingOne ST / AIC IGA journey node invocation." + + - id: T-12 + prompt: "I need to call the PingOne Protect risk evaluation API from a PingFederate custom adapter — how do I integrate it?" + expected_anchors: + - references/curated/service-invocation-patterns.md + - references/curated/cross-platform-service-usage.md + expected_tier: curated + notes: "Ping Software Suite REST API invocation of Protect." + + # ----- Strategy doc § 9 use cases (Getting Started 0–30) ----- + + - id: T-50 + prompt: "Add risk-based security with PingOne Protect to detect suspicious logins and trigger step-up." + expected_anchors: + - plugins/ping-identity/skills/ping-universal-services/references/curated/protect-configuration.md + expected_tier: curated + notes: "strategy-uc-5 (Protect)" + + - id: T-51 + prompt: "Add identity verification (KYC) using PingOne Verify during customer onboarding." + expected_anchors: + - plugins/ping-identity/skills/ping-universal-services/references/curated/verify-configuration.md + expected_tier: curated + notes: "strategy-uc-5 (Verify)" + +non_trigger_prompts: + - id: N-01 + prompt: "Set up the PingOne MT environment and register an OIDC application." + expected_skill: ping-foundation + notes: "Admin setup — not a shared service invocation." + + - id: N-02 + prompt: "Build a login journey in AIC with email OTP and a username/password node." + expected_skill: ping-orchestration + notes: "Flow/journey design without any named Universal Service invocation." + + - id: N-03 + prompt: "How do I configure the PingFederate LDAP data store connection to my Active Directory?" + expected_skill: ping-foundation + notes: "Platform admin configuration — PingFederate data store setup." + + - id: N-04 + prompt: "Integrate the PingOne Protect JavaScript SDK into my React single-page application." + expected_skill: ping-app-integration + notes: "SDK/app integration task — not service orchestration." + + - id: N-05 + prompt: "Design a DaVinci flow for progressive profiling that collects user attributes across three login sessions." + expected_skill: ping-orchestration + notes: "DaVinci flow design — no Universal Service invocation." + +ambiguous_prompts: + - id: A-01 + prompt: "I need to add verification to my registration flow." + expected_clarification_keywords: + - verify + - protect + - risk + - identity proofing + - mfa + - otp + - service + notes: "Verification could mean MFA (orchestration) or KYC (universal-services)." + + - id: A-02 + prompt: "My users are getting suspicious login attempts — how do I add security to my AIC journey?" + expected_clarification_keywords: + - protect + - risk + - mfa + - signal + - fraud + - threat + - score + notes: "Could be PingOne Protect (universal-services) or just adding MFA (orchestration); needs clarification on whether risk scoring or a second factor is the goal." + + - id: A-03 + prompt: "A user says they can't verify their identity when they try to log in — is this a Verify issue or an MFA issue?" + expected_clarification_keywords: + - verify + - mfa + - document + - liveness + - otp + - push + - second factor + - identity proofing + notes: "Could route to universal-services (Verify) or ping-orchestration (MFA); distinction depends on whether document/liveness check or second authentication factor is involved." diff --git a/evals/results/.gitkeep b/evals/results/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/evals/results/2026-06-01/claude.layer1.json b/evals/results/2026-06-01/claude.layer1.json new file mode 100644 index 0000000..efe9d75 --- /dev/null +++ b/evals/results/2026-06-01/claude.layer1.json @@ -0,0 +1,44 @@ +{ + "ping-app-integration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-foundation": { + "layer": 1, + "trigger": 0.9, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-identity-for-ai": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-orchestration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-quickstart": { + "layer": 1, + "trigger": 0.9, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-universal-services": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + } +} \ No newline at end of file diff --git a/evals/results/2026-06-02/claude.layer1.json b/evals/results/2026-06-02/claude.layer1.json new file mode 100644 index 0000000..efe9d75 --- /dev/null +++ b/evals/results/2026-06-02/claude.layer1.json @@ -0,0 +1,44 @@ +{ + "ping-app-integration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-foundation": { + "layer": 1, + "trigger": 0.9, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-identity-for-ai": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-orchestration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-quickstart": { + "layer": 1, + "trigger": 0.9, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-universal-services": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + } +} \ No newline at end of file diff --git a/evals/results/2026-06-03/claude.layer1.json b/evals/results/2026-06-03/claude.layer1.json new file mode 100644 index 0000000..93d259c --- /dev/null +++ b/evals/results/2026-06-03/claude.layer1.json @@ -0,0 +1,44 @@ +{ + "ping-app-integration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-foundation": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-identity-for-ai": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-orchestration": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-quickstart": { + "layer": 1, + "trigger": 0.9, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + }, + "ping-universal-services": { + "layer": 1, + "trigger": 1.0, + "non_trigger": 1.0, + "ambiguous": 1.0, + "passed": true + } +} \ No newline at end of file diff --git a/evals/schemas/prompt-set.schema.json b/evals/schemas/prompt-set.schema.json new file mode 100644 index 0000000..8545194 --- /dev/null +++ b/evals/schemas/prompt-set.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Skill prompt set", + "type": "object", + "required": ["skill", "version", "trigger_prompts", "non_trigger_prompts", "ambiguous_prompts"], + "additionalProperties": false, + "properties": { + "skill": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" }, + "version": { "type": "integer", "minimum": 1 }, + "trigger_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_anchors", "expected_tier"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^T-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 10 }, + "expected_anchors": { "type": "array", "items": { "type": "string" } }, + "expected_tier": { "type": "string", "enum": ["curated", "generated", "docs-mcp"] }, + "notes": { "type": "string" } + } + } + }, + "non_trigger_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_skill"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^N-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 10 }, + "expected_skill": { "type": ["string", "null"] }, + "notes": { "type": "string" } + } + } + }, + "ambiguous_prompts": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "prompt", "expected_clarification_keywords"], + "additionalProperties": false, + "properties": { + "id": { "type": "string", "pattern": "^A-[0-9]{2,3}$" }, + "prompt": { "type": "string", "minLength": 5 }, + "expected_clarification_keywords": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "notes": { "type": "string" } + } + } + } + } +} diff --git a/evals/scorecards/anchor-selection-eval.md b/evals/scorecards/anchor-selection-eval.md new file mode 100644 index 0000000..2f68abf --- /dev/null +++ b/evals/scorecards/anchor-selection-eval.md @@ -0,0 +1,52 @@ +--- +title: Anchor selection eval — Layer 2 +status: current +last_updated: 2026-05-29 +--- + +# Anchor Selection Eval (Layer 2) + +Used by `evals/harness/run_eval.py --layer 2` to score whether the agent loaded the right curated reference files within an active skill. + +## Inputs + +- `evals/prompts/.yaml` — every `trigger_prompts[*]` entry with non-empty `expected_anchors`. +- The agent's tool-call log for the run, captured by the LLM adapter. + +## Score per prompt + +``` +loaded = set of paths the agent passed to Read(...) +expected = set of paths in expected_anchors +precision = |loaded ∩ expected| / |loaded| (1.0 if both sets are empty) +recall = |loaded ∩ expected| / |expected| +pass = recall >= 1.0 AND precision >= 0.5 +``` + +## Aggregate per skill + +- `pass_rate = passed_prompts / total_prompts_with_expected_anchors` +- **Pass bar:** `pass_rate >= 0.85` + +## Failure output + +For each failing prompt the harness prints: + +``` +[FAIL] T-04 (ping-orchestration) + expected: plugins/ping-identity/skills/ping-orchestration/references/curated/journey-design-patterns.md + loaded: plugins/ping-identity/skills/ping-orchestration/references/curated/davinci-flow-patterns.md + recall=0.0 precision=0.0 +``` + +## Common failure modes + +| Failure | Likely cause | Fix | +|---|---|---| +| recall=0 always | SKILL.md doesn't reference the expected anchor by path | Add anchor path to the SKILL.md retrieval list | +| recall=1 but precision<0.5 | SKILL.md loads too many anchors per prompt | Tighten routing table; split anchors | +| Inconsistent across runs | LLM non-determinism | Run with temperature=0; if still inconsistent, reword SKILL.md | + +## Why precision and recall + +Strategy doc § 0 mandates "use the smallest trusted context first." Loading the wrong anchor wastes tokens; missing the right one produces a vague answer. Both must be measured — reporting only recall hides over-retrieval. diff --git a/evals/scorecards/plan-quality-eval.md b/evals/scorecards/plan-quality-eval.md new file mode 100644 index 0000000..30448b8 --- /dev/null +++ b/evals/scorecards/plan-quality-eval.md @@ -0,0 +1,71 @@ +--- +title: Plan quality eval — Layer 3 +status: current +last_updated: 2026-05-29 +--- + +# Plan Quality Eval (Layer 3) + +Used by `evals/harness/judge_plans.py` to score the textual plan a skill produces against a hand-authored golden plan. + +## Inputs + +- `evals/prompts/.yaml` — `trigger_prompts[*]` with a corresponding `evals/golden//.md` file. +- The full final assistant message from the skill being evaluated. +- A judge LLM (Claude / Codex / Gemini), **DIFFERENT** from the LLM under test. + +## Judge prompt template + +``` +You are scoring an agent's response against a reference plan. + +USER PROMPT: + + +REFERENCE PLAN (golden): + + +AGENT'S PRODUCED PLAN: + + +Score 1–5 on each dimension. Output ONLY valid JSON: +{ + "correctness": {"score": , "reason": "<≤200 chars>"}, + "completeness": {"score": , "reason": "<≤200 chars>"}, + "concreteness": {"score": , "reason": "<≤200 chars>"}, + "tier_discipline": {"score": , "reason": "<≤200 chars>", + "expected": "", + "observed": ""} +} + +Rubric: +- correctness: factual accuracy about Ping products, services, fields +- completeness: covers every required step in the reference +- concreteness: names specific products, fields, env vars; no generic prose +- tier_discipline: matches expected_tier per rules/runtime-selection.md; + did not over-escalate; did not under-load +``` + +## Aggregate per skill + +``` +mean_correctness = mean of all correctness scores +mean_completeness = mean of all completeness scores +mean_concreteness = mean of all concreteness scores +mean_tier = mean of all tier_discipline scores + +pass = all four means >= 4.0 AND no individual score < 3 +``` + +## Why a different judge model + +Self-judging collapses the eval — a model rates its own wording highly because it matches its priors. Defaults: +- Claude under test → Codex judges +- Codex under test → Claude judges +- Gemini under test → Claude judges + +Override with `--judge `. + +## Schedule + +Layer 3 runs weekly and before any release. It is NOT a per-PR gate in Phase 0 or Phase 1. It becomes a pre-release requirement from Phase 2 onward. diff --git a/evals/scorecards/routing-eval.md b/evals/scorecards/routing-eval.md new file mode 100644 index 0000000..a88345c --- /dev/null +++ b/evals/scorecards/routing-eval.md @@ -0,0 +1,243 @@ +--- +name: ping-routing-eval +description: Evaluate a Ping Identity skill-routing system run. Use this whenever you need to test whether an agent chose the right skill, the right platform branch, the right retrieval tier, and gave a correct answer — all while staying token-efficient. Invoke this eval format for any benchmark prompt before shipping a skill update. +--- + +# Ping Identity — Skill Routing Eval (Layer 1) + +## Layer 1 in the harness + +This scorecard is the human-readable rubric for Layer 1 (routing accuracy). The automated harness implementation is `evals/harness/run_eval.py --layer 1`. It consumes `evals/prompts/.yaml` validated against `evals/schemas/prompt-set.schema.json`. CI runs Layer 1 from Phase 3 onward; PRs failing the pass bar (90% trigger / 90% non-trigger / 80% ambiguous) are blocked. + +The 6 v1 umbrella skills are: `ping-quickstart`, `ping-foundation`, `ping-orchestration`, `ping-universal-services`, `ping-app-integration`, `ping-identity-for-ai`. + +--- + +You are evaluating a skill-routing system for Ping Identity agent skills. + +Your job is not just to answer the user's question. Your job is to evaluate the retrieval path and the answer at the same time. + +--- + +## How to run an eval + +For each benchmark prompt, complete all steps below in order. Do not skip steps. + +--- + +## Step 1 — Choose the primary umbrella skill + +Select one: +- `ping-quickstart` +- `ping-foundation` +- `ping-orchestration` +- `ping-universal-services` +- `ping-app-integration` +- `ping-identity-for-ai` + +**Skill selection guide:** + +| Signal in prompt | Likely skill | +|---|---| +| "Where do I start?", "Which product?", platform unknown | `ping-quickstart` | +| "Set up", "configure", "admin", "create environment", "add app", "install", "directory" | `ping-foundation` | +| "Build a flow", "journey", "DaVinci", "authentication tree", "scripted node", "registration flow" | `ping-orchestration` | +| "Protect", "Verify", "IGA", "Credentials", "Authorize", "risk score", "identity proofing" | `ping-universal-services` | +| "SDK", "mobile", "React", "iOS", "Android", "integrate my app", "hosted login page" | `ping-app-integration` | +| "AI agent identity", "Verified Trust", "MCP server auth", "identity for AI" | `ping-identity-for-ai` | + +--- + +## Step 2 — Identify the skill route + +State: +- **Primary umbrella skill** and why it wins +- **Secondary plausible skills**, if more than one could fit +- Why the primary should win over any secondary + +--- + +## Step 3 — Choose the platform branch + +Select one: +- `pingone-mt` — PingOne (multi-tenant cloud, apps.pingone.com) +- `pingone-st` — PingOne ST / AIC (single-tenant, PingAM/PingIDM/PingDS) +- `ping-software` — PingFederate, PingAccess, PingDirectory, PingAM standalone +- `cross-platform` — genuinely spans two or more families + +Then select the **product or service branch** within the platform (e.g., DaVinci, PingFederate, PingOne Verify). + +Note any secondary branch candidates if the prompt is ambiguous. + +--- + +## Step 4 — Decide the minimum retrieval path + +Choose the smallest tier that resolves the prompt: + +| Tier | Use when | +|---|---| +| **Curated refs only** | The curated anchor(s) for the skill + platform fully answer the question | +| **Curated refs + bounded shortlist** | Curated anchors orient but do not cover the specific task; a generated shortlist provides the gap | +| **Docs fallback only if still necessary** | Neither curated nor shortlist is sufficient; a targeted external query is required | + +**Retrieval discipline rules:** +- Load the smallest trusted context first +- Do not load more than 3 curated refs unless the task explicitly spans multiple domains +- Do not open a generated shortlist if a curated anchor already resolves the task +- Never load external docs if curated refs are sufficient +- Stop as soon as the answer is good enough + +State: +- Retrieval tier used +- Curated refs you would load (by path) +- Shortlist refs you would load (by path), if any +- Whether external docs are needed: Yes / No +- Estimated retrieval token spend +- Estimated total token spend +- Why this is the minimum sufficient context + +--- + +## Step 5 — Answer the benchmark prompt + +Provide the actual answer as if you were the agent. Answer at the level of detail the prompt requires — do not over-explain, and do not under-answer. + +--- + +## Step 6 — Score the run + +Use this rubric. Score each dimension, then sum. + +| Dimension | Max | Criteria | +|---|---|---| +| **Routing Correctness** | 30 | Correct umbrella skill selected; platform branch correct; secondary skills identified correctly | +| **Context Correctness** | 25 | Right curated files loaded; no irrelevant files loaded; shortlist used only when needed | +| **Answer Correctness** | 20 | Factually correct; matches platform; complete enough to act on | +| **Token Efficiency** | 15 | Minimum sufficient context; no over-retrieval; answer appropriately scoped | +| **Fallback Discipline** | 10 | No unnecessary external docs call; fallback used only when curated + shortlist are genuinely insufficient | + +**Scoring guidance:** +- Deduct 15–30 points from Routing Correctness for wrong umbrella skill on an easy case +- Deduct 10–20 points from Answer Correctness for a major factual error +- Deduct all 10 Fallback Discipline points for an unnecessary external docs call +- Deduct 10–15 points from Token Efficiency if retrieval exceeds 150% of minimum needed + +--- + +## Step 7 — Hard gate pass/fail + +A run **FAILS** if any of the following is true: +- Wrong umbrella skill on an easy case (unambiguous prompt) +- Major factual error in the answer +- Unnecessary external docs call on an easy case +- Token spend above 150% of the estimated minimum +- Final score below 80/100 + +--- + +## Output format + +Use this exact format for every eval run: + +``` +# Skill Routing Eval + +## Benchmark Prompt +[paste the benchmark prompt here] + +## Route Decision +- Primary umbrella skill: +- Secondary plausible skills: +- Platform branch: +- Product or service branch: +- Secondary branch candidates: +- Confidence: High / Medium / Low + +## Retrieval Plan +- Retrieval tier used: +- Curated refs to load: +- Shortlist refs to load: +- Docs fallback needed: Yes / No +- Estimated retrieval token spend: +- Estimated total token spend: +- Why this is the minimum sufficient context: + +## Answer +[Provide the actual answer to the benchmark prompt] + +## Scorecard +- Routing Correctness: X/30 +- Context Correctness: X/25 +- Answer Correctness: X/20 +- Token Efficiency: X/15 +- Fallback Discipline: X/10 +- Final Score: X/100 + +## Pass/Fail +- Result: PASS or FAIL +- Hard fail reason, if any: + +## Short Rationale +- What you routed correctly: +- What context you intentionally did not load: +- Whether fallback was disciplined: +- Biggest risk in this run: +``` + +--- + +## Benchmark prompt library + +Use these prompts to validate skill routing. Add new prompts as new skills and references are built. Each prompt should be a realistic, substantive user message — not a generic one-liner. + +### Easy cases (should route confidently) + +**E-01 — Foundation, PingOne ST** +> I need to register a new OIDC application in my AIC tenant so my React app can use it for login. What do I need to configure? + +**E-02 — Orchestration, PingOne ST** +> I want to build a registration journey in PingOne ST that collects the user's email, sends a verification OTP, and creates a managed object on success. What nodes do I need and in what order? + +**E-03 — Quickstart** +> My company is evaluating Ping Identity. We're not sure whether to go with PingOne or the on-premises software. What questions should we be asking? + +**E-04 — Foundation, Ping Software** +> We're installing PingFederate on-prem and need to configure an SP connection for SAML SSO to Salesforce. Where do I start? + +**E-05 — Orchestration, PingOne MT** +> I need to build a DaVinci flow that does username/password login, checks risk, and does an MFA step-up if risk is medium or high. + +### Medium cases (some ambiguity) + +**M-01 — Foundation + Orchestration boundary** +> I want to add an MFA policy to my PingOne MT environment. Is that a policy configuration or do I need to build a flow? + +**M-02 — Orchestration + Universal Services boundary** +> I'm building an AIC journey and want to add PingOne Verify for identity proofing during registration. What does that involve? + +**M-03 — App Integration** +> We have a PingOne ST tenant set up and a journey ready. Now I need to integrate the hosted login page into our iOS app using the Ping SDK. + +### Hard cases (multi-skill or cross-platform) + +**H-01 — Full stack** +> We're building a CIAM solution on PingOne ST: tenant provisioned, apps registered, now we need a registration journey with email OTP, identity verification via PingOne Verify, and then the iOS SDK integration. What's the sequence of skills and what does each one own? + +**H-02 — Cross-platform ambiguity** +> We're running PingFederate on-prem and want to add PingOne MFA as a step-up. How do these two products connect? + +--- + +## Guidance for writing new benchmark prompts + +From Anthropic's skill-creator guidance: "Query quality matters — create realistic, substantive prompts with context (file names, column values, backstory), not generic requests." + +A good benchmark prompt: +- Reads like a real user message, not a test question +- Has enough context to make routing unambiguous (easy cases) or intentionally ambiguous (medium/hard cases) +- Has one clearly correct primary skill on easy cases +- Tests a specific curated reference or routing decision you want to validate +- Is not solvable from general LLM knowledge alone — it requires the skill's curated context to answer well + +A poor benchmark prompt is a one-liner like "How do I set up PingOne?" — too vague to test routing precision. diff --git a/helix-headless-agentic-workflows-aic-davinci.md b/helix-headless-agentic-workflows-aic-davinci.md new file mode 100644 index 0000000..73e05b5 --- /dev/null +++ b/helix-headless-agentic-workflows-aic-davinci.md @@ -0,0 +1,98 @@ +# Helix for Headless Agentic Workflows in AIC and DaVinci + +## Executive Summary + +Helix is the right orchestration layer for a headless agentic workflow across both AIC and DaVinci. The most practical pattern is to run agent logic in Helix, invoke it remotely via Helix conversation and message APIs, and let the agent call product APIs through custom tools and functions. For AIC, this is the clearest production-shaped pattern today. For DaVinci, it aligns directly with the current Helix-backed assistant direction. citation_153:1,citation_247:0,citation_247:1,citation_247:3,citation_247:5,citation_247:6,citation_195:13,citation_195:19 + +## What Helix Provides + +Helix supports outbound auth using agent identity or OAuth client credentials. It also supports tool and function integrations via `get_security_headers()`, which allows downstream API calls with the right security context. Helix also supports token passthrough so user context can propagate into agent execution when needed. citation_205:6,citation_205:55,citation_205:73,citation_198:37,citation_198:38 + +The practical execution model is: + +1. Start a Helix conversation. +2. Post a start message. +3. Poll for completion. +4. Let the agent call AIC or DaVinci APIs through Helix tools. +5. Return results, recommended actions, or generated artifacts such as flow JSON. citation_247:0,citation_247:1,citation_247:3,citation_247:5,citation_247:6 + +## AIC: Recommended Headless Pattern + +For AIC, the recommended pattern is Helix-native API orchestration. + +### Suggested setup + +- Make AIC the IDP for the Helix environment. citation_205:2 +- Give the agent either agent identity or AIC service-account / OAuth credentials. citation_205:6,citation_206:34 +- Implement Helix custom tools or functions that call AIC REST APIs using Helix security headers. citation_205:55,citation_205:73 +- Invoke the agent headlessly from a backend service, CI job, scheduler, or another orchestration layer via Helix conversation APIs. citation_247:0,citation_247:1,citation_247:3 + +### Good use cases + +- AIC config and admin automation +- Journey, application, and managed-object inspection +- Usage and audit-oriented analysis +- Guided write workflows with policy checks before commit citation_207:68,citation_207:69,citation_207:72 + +### Key limitation + +The current AIC MCP server is not yet the strongest basis for unattended headless runtime. There is an explicit request for headless support, and current modes still depend on interactive auth flows. Internal guidance also says the AIC MCP server should not be used against production. citation_214:1,citation_214:7,citation_214:8,citation_215:52 + +### Recommendation + +Use Helix directly for production-oriented headless AIC workflows. Treat the AIC MCP server as a developer-assistant or sandbox path for now. citation_214:8,citation_215:52 + +## DaVinci: Recommended Headless Pattern + +For DaVinci, Helix is an even cleaner fit because the product direction already assumes a Helix-backed assistant pattern. + +### Suggested setup + +- DaVinci, or a thin backend facade, creates a Helix conversation against a published agent version. citation_164:8,citation_164:11 +- It sends the prompt plus current flow and environment context. citation_195:13,citation_195:19 +- Helix returns complete flow JSON updates. citation_195:19,citation_195:32,citation_195:46 +- DaVinci validates dependencies, versions the result, and only applies changes after policy or human-in-the-loop gates. citation_195:38,citation_195:46 + +This matches the current build-use-case direction: Helix asks clarifying questions, DaVinci consumes generated flow JSON, and the canvas is locked while generation is in progress. citation_195:16,citation_195:19,citation_195:38 + +### Key limitation + +DaVinci has evaluated Helix-hosted remote MCP, but the decision to use it is still open. A custom remote MCP path is also being explored. citation_196:5,citation_196:6 + +### Recommendation + +Use Helix agent APIs first for DaVinci headless execution. Do not make Helix-hosted remote MCP the primary production path until that evaluation closes. citation_196:5,citation_196:6 + +## Shared Architecture Recommendation + +Use the same core model for both AIC and DaVinci: + +- Helix as the orchestration and runtime layer +- Product-specific custom Helix tools as the execution layer +- Helix conversation APIs as the async control plane +- Policy and approval gates before write or apply operations +- Published agent versions for stability and repeatability citation_153:1,citation_247:0,citation_247:1,citation_247:3,citation_247:5,citation_164:11 + +### AIC flow + +Scheduler or backend service -> Helix agent -> AIC-facing custom tools -> AIC APIs -> result or proposed change set. citation_205:55,citation_205:73,citation_247:0 + +### DaVinci flow + +DaVinci UI or backend service -> Helix build or troubleshoot agent -> DaVinci-facing tools -> flow generation, validation, or troubleshooting output -> review and apply. citation_195:13,citation_195:19,citation_195:32,citation_195:46 + +## Practical Implementation Guidance + +If one implementation path is needed for both products, use this sequence: + +1. Standardize on Helix as the headless runtime. citation_153:1,citation_247:0 +2. Build product-specific tool surfaces: + - `aic_*` tools for AIC REST and admin operations + - `davinci_*` tools for flow build, inspect, and troubleshoot operations +3. Invoke all agents through the Helix conversation APIs. +4. Keep MCP as a secondary interoperability surface for developer tooling and external agent ecosystems. +5. Put write operations behind policy and approval gates, especially where token passthrough or user context is involved, because Helix threat modeling explicitly calls out excessive-agency risk. citation_198:37,citation_198:38,citation_231:31 + +## Final Recommendation + +If the goal is a real headless agentic workflow across both AIC and DaVinci, the architecture should be centered on Helix agents, Helix APIs, and product-specific tools. MCP should be additive, not central. That gives the cleanest and most consistent operating model across both platforms today. citation_214:8,citation_196:6,citation_247:0,citation_205:55 diff --git a/plugins/ping-identity/.claude-plugin/plugin.json b/plugins/ping-identity/.claude-plugin/plugin.json new file mode 100644 index 0000000..ad2d3f9 --- /dev/null +++ b/plugins/ping-identity/.claude-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "ping-identity", + "version": "1.0.0", + "description": "Core Ping Identity platform skills: ping-quickstart, ping-foundation, ping-orchestration, ping-universal-services, ping-app-integration, ping-identity-for-ai.", + "author": { "name": "Ping Identity" } +} diff --git a/plugins/ping-identity/README.md b/plugins/ping-identity/README.md new file mode 100644 index 0000000..f6f7771 --- /dev/null +++ b/plugins/ping-identity/README.md @@ -0,0 +1,57 @@ +# Ping Identity Plugin + +Agent skills for configuring, integrating, and operating Ping Identity platforms. + +## What this plugin is for + +- Orienting agents on which Ping platform applies to a given task +- Configuring and administering PingOne MT (multi-tenant cloud), PingOne ST (single-tenant), and on-premises Ping software +- Designing orchestration flows (DaVinci, PingOne ST journeys, PingAM trees) +- Integrating Ping into web, mobile, and SDK-based applications +- Using Ping Universal Services (Protect, Verify, IGA, Credentials, Authorize) +- Understanding Ping's Identity for AI and Verified Trust capabilities + +## Skill role: context, not execution + +MCP tools handle execution — they can create, update, and delete platform resources directly. These skills supply what the tools lack: platform architecture, correct configuration sequencing, key constraints, gotchas, and decision logic. Use skills to reason correctly about *what* to configure and *why*, then let MCP tools carry out the work. + +## MCP tool-first execution + +**If MCP tools are available for the target platform, always use them — do not instruct the user to make changes manually.** + +Before responding to any configuration or administration request, scan your available tool list for tools that can perform the required operation against the target platform (PingOne ST, PingOne MT, etc.). If matching tools exist, use them to make the change directly. + +**Execution order:** +1. Check available tools for operations matching the task (create, update, delete, list, get for the relevant resource type) +2. If found: use the tool, report what was done, show relevant IDs or output +3. If not found or the tool call fails: fall back to step-by-step console instructions + +**Never ask the user to click through the admin console for an operation an MCP tool can perform.** A direct tool call is always preferred over written instructions. + +## What this plugin is NOT for + +- Generic identity concepts not specific to Ping products +- Non-Ping identity platforms (Okta, Auth0, Microsoft Entra, etc.) +- Application business logic unrelated to identity +- Infrastructure provisioning beyond what is required to deploy Ping software + +## Standalone operation + +This plugin is self-orienting. When installed without the full `agent-skills` repo, it carries its own: + +- Platform and product scope (`platform-scope.md`) +- Skill map and routing (`plugin-map.md`, `routing-hints.md`) +- Reference index (`references/index.json`) + +Load order for a plugin-only install: +1. `README.md` → orientation +2. `plugin-map.md` → skill selection +3. `platform-scope.md` → platform detection +4. `routing-hints.md` → routing fallback (replaces `/shared/taxonomies/routing-rules.md`) +5. Selected `skills//SKILL.md` +6. `ping-quickstart`: load from `skills/ping-quickstart/references/` (flat — 1–2 files max) +7. Other skills: `skills//references/curated/` → 1–3 curated anchors, then `references/generated//` if needed + +## Full repo + +When the full `agent-skills` repo is present, defer to `/shared/taxonomies/` for canonical routing rules and platform definitions. The files in this plugin are a compact local subset — the full repo is authoritative. diff --git a/plugins/ping-identity/platform-scope.md b/plugins/ping-identity/platform-scope.md new file mode 100644 index 0000000..091e0c8 --- /dev/null +++ b/plugins/ping-identity/platform-scope.md @@ -0,0 +1,49 @@ +# Platform Scope — Ping Identity Plugin + +Defines which platforms, products, and services are in scope. Use for platform detection when `/shared/taxonomies/platform-families.md` is not available. + +## Platform Families + +| Tag | Platform | Description | +|---|---|---| +| `pingone-mt` | PingOne (multi-tenant cloud) | SaaS-hosted administration: environments, apps, directories, policies. Admin at apps.pingone.com. | +| `pingone-st` | PingOne ST (single-tenant) | Fully managed, highly customizable. Built on PingAM/PingIDM/PingDS. Distinct control plane from PingOne. | +| `ping-software` | Ping Software Suite (on-premises) | Customer-deployed server software. Different topology, ops model, and config surface from cloud families. Includes PingAM, PingIDM, PingDS, PingGateway, PingFederate, etc. | +| `cross-platform` | Shared / Universal Services | Capabilities consumed across multiple platform families (Protect, Verify, IGA, etc.). | + +## Platform Detection Signals + +**`pingone-mt`** +- "Multi-Tenant" +- "PingOne", "apps.pingone.com", "PingOne environment", "PingOne admin console" +- PingOne MFA, PingOne Risk, PingOne DaVinci, PingOne Verify, PingOne Protect, PingOne IGA, PingOne Credentials, PingOne Neo, PingOne Authorize, PingOne SSO + +**`pingone-st`** +- "Single-Tenant" +- "PingOne ST", "AIC" (legacy abbreviation), "identity cloud tenant", "PingAM", "PingIDM", "PingDS" +- "ForgeRock", "AM", "IDM", "DS" (in a PingOne ST Cloud context), PingOne ST tenant URL +- Journeys, auth trees, realms + +**`ping-software`** +- "On-prem", "Software", "Download", "Binary", ".jar", "PingFederate", "PingAccess", "PingDirectory", "PingDataSync", "PingID on-prem", "PingAM standalone", "on-prem", "self-managed", "server profile" + +**`cross-platform`** +- Service invoked from both PingOne MT and PingOne ST contexts +- Universal Services layer questions (not product-specific setup) + +## Products in Scope + +### PingOne (multi-tenant) +PingOne, PingOne MFA, PingOne Risk, PingOne DaVinci, PingOne Verify, PingOne Protect, PingOne IGA, PingOne Credentials, PingOne Neo, PingOne Authorize, PingOne SSO, PingOne Notifications + +### PingOne ST +PingOne ST, PingAM (within PingOne ST), PingIDM (within PingOne ST), PingDS (within PingOne ST) + +### Ping Software Suite +PingFederate, PingAccess, PingDirectory, PingDataSync, PingID (on-prem), PingAM (standalone), PingIDM (standalone), PingDS (standalone), PingAuthoriz, PingGateway + +## Services Out of Scope for This Plugin + +- Non-Ping identity providers +- Generic OIDC/SAML implementations not using a Ping product +- Cloud infrastructure (AWS, GCP, Azure) beyond what Ping software runs on diff --git a/plugins/ping-identity/plugin-map.md b/plugins/ping-identity/plugin-map.md new file mode 100644 index 0000000..852a254 --- /dev/null +++ b/plugins/ping-identity/plugin-map.md @@ -0,0 +1,40 @@ +# Plugin Map — Ping Identity + +Index of included skills, their purpose, and when to use each. Use this file to select the correct skill before loading any SKILL.md. + +## Skills + +### ping-quickstart +**Path:** `skills/ping-quickstart/` +**Use when:** The user does not know which Ping platform or product applies, or explicitly asks "where do I start?" +**Not for:** Any task where the platform is already known — go directly to the right skill. + +### ping-foundation +**Path:** `skills/ping-foundation/` +**Use when:** The task is setup, configuration, or administration of a platform — tenant/environment creation, app registration, directory setup, authentication policy, branding. +**Not for:** Designing flows or journeys (→ ping-orchestration); invoking shared services (→ ping-universal-services); app/SDK code integration (→ ping-app-integration). + +### ping-orchestration +**Path:** `skills/ping-orchestration/` +**Use when:** The task is designing or troubleshooting a DaVinci flow, PingOne ST journey, or PingAM authentication tree. + +### ping-universal-services +**Path:** `skills/ping-universal-services/` +**Use when:** The task involves invoking a Ping Universal Service: PingOne Protect (risk scoring), PingOne Verify (identity verification/KYC), PingOne Credentials, IGA, Neo, or Authorize — across PingOne MT or PingOne ST. Also use for cross-platform shared service orchestration and service invocation patterns. + +### ping-app-integration +**Path:** `skills/ping-app-integration/` +**Use when:** The task is integrating Ping into an application — Android SDK, iOS SDK, React SDK, DaVinci SDK, browser flows, or on-prem app-side configuration. Covers mobile and web SDK setup, OIDC/OAuth wiring, and integration troubleshooting. + +### ping-identity-for-ai +**Path:** `skills/ping-identity-for-ai/` +**Use when:** The task involves securing AI agents with Ping Identity, implementing Verified Trust, building workforce helpdesk AI (password reset, account unlock), or applying identity security patterns to AI-driven applications and agentic workflows. + +## Selection rule + +1. Unknown platform or starting point → `ping-quickstart` +2. Setup / config / admin → `ping-foundation` +3. Flow / journey / orchestration design → `ping-orchestration` +4. Shared service invocation → `ping-universal-services` +5. App / SDK integration code → `ping-app-integration` +6. AI agent or trusted identity patterns → `ping-identity-for-ai` diff --git a/plugins/ping-identity/references/index.json b/plugins/ping-identity/references/index.json new file mode 100644 index 0000000..51829e0 --- /dev/null +++ b/plugins/ping-identity/references/index.json @@ -0,0 +1,118 @@ +{ + "plugin": "ping-identity", + "description": "Local reference index for plugin-only installs. Lists all curated anchors and generated shortlists by skill and platform branch.", + "last_updated": "2026-06-03", + "skills": { + "ping-quickstart": { + "curated": [ + "skills/ping-quickstart/references/curated/getting-started-overview.md", + "skills/ping-quickstart/references/curated/choose-the-right-ping-platform.md", + "skills/ping-quickstart/references/curated/common-starting-patterns.md", + "skills/ping-quickstart/references/curated/pingone-licensing-and-packaging.md", + "skills/ping-quickstart/references/curated/forgerock-to-ping-migration-paths.md", + "skills/ping-quickstart/references/curated/inherited-deployment-orientation.md", + "skills/ping-quickstart/references/curated/end-to-end-validation.md" + ] + }, + "ping-foundation": { + "curated": [ + "skills/ping-foundation/references/curated/cross-platform/foundation-overview.md", + "skills/ping-foundation/references/curated/cross-platform/tenant-and-environment-setup.md", + "skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md", + "skills/ping-foundation/references/curated/cross-platform/core-admin-patterns.md", + "skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md", + "skills/ping-foundation/references/curated/pingone-mt/app-registration.md", + "skills/ping-foundation/references/curated/pingone-mt/sign-on-policies.md", + "skills/ping-foundation/references/curated/pingone-mt/directory-and-populations.md", + "skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md", + "skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md", + "skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md", + "skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md", + "skills/ping-foundation/references/curated/ping-software/pingaccess-basics.md", + "skills/ping-foundation/references/curated/pingone-st/foundation-overview.md", + "skills/ping-foundation/references/curated/pingone-st/app-setup.md", + "skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md", + "skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md", + "skills/ping-foundation/references/curated/pingone-st/directory-setup.md" + ], + "generated": { + "pingone-mt": "skills/ping-foundation/references/generated/pingone-mt/top-25.json", + "pingone-st": "skills/ping-foundation/references/generated/pingone-st/top-25.json", + "ping-software": "skills/ping-foundation/references/generated/ping-software/top-25.json", + "cross-platform": "skills/ping-foundation/references/generated/cross-platform/top-10.json" + } + }, + "ping-orchestration": { + "curated": [ + "skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/basic-auth-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/risk-management-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/identity-management-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/utility-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/nodes/federation-contextual-nodes.md", + "skills/ping-orchestration/references/curated/pingone-st/routing-index.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/social-and-local-registration-authentication.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/password-reset-and-update.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/financial-services-step-up.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/progressive-profiling.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/pingone-protect-risk-integration.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/mfa-authentication-multi-method.md", + "skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/account-recovery-and-username-reminder.md", + "skills/ping-orchestration/references/curated/pingone-mt/davinci-overview.md", + "skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md", + "skills/ping-orchestration/references/curated/pingone-mt/davinci-registration-and-mfa.md", + "skills/ping-orchestration/references/curated/cross-platform/passkeys-and-passwordless.md" + ], + "generated": { + "pingone-mt": "skills/ping-orchestration/references/generated/pingone-mt/top-25.json", + "pingone-st": "skills/ping-orchestration/references/generated/pingone-st/top-25.json", + "ping-software": "skills/ping-orchestration/references/generated/ping-software/top-25.json", + "cross-platform": "skills/ping-orchestration/references/generated/cross-platform/top-10.json" + } + }, + "ping-universal-services": { + "curated": [ + "skills/ping-universal-services/references/curated/universal-services-overview.md", + "skills/ping-universal-services/references/curated/choosing-the-right-service.md", + "skills/ping-universal-services/references/curated/service-invocation-patterns.md", + "skills/ping-universal-services/references/curated/cross-platform-service-usage.md", + "skills/ping-universal-services/references/curated/protect-configuration.md", + "skills/ping-universal-services/references/curated/verify-configuration.md" + ], + "generated": { + "protect": "skills/ping-universal-services/references/generated/protect/.gitkeep", + "verify": "skills/ping-universal-services/references/generated/verify/.gitkeep", + "credentials": "skills/ping-universal-services/references/generated/credentials/.gitkeep", + "sso": "skills/ping-universal-services/references/generated/sso/.gitkeep", + "iga": "skills/ping-universal-services/references/generated/iga/.gitkeep" + } + }, + "ping-app-integration": { + "curated": [ + "skills/ping-app-integration/references/curated/app-integration-overview.md", + "skills/ping-app-integration/references/curated/mobile-integration-basics.md", + "skills/ping-app-integration/references/curated/web-integration-basics.md", + "skills/ping-app-integration/references/curated/server-side-integration-basics.md", + "skills/ping-app-integration/references/curated/integration-troubleshooting-basics.md" + ], + "generated": { + "mobile": "skills/ping-app-integration/references/generated/mobile/.gitkeep", + "web": "skills/ping-app-integration/references/generated/web/.gitkeep", + "orchestration-sdks": "skills/ping-app-integration/references/generated/orchestration-sdks/.gitkeep", + "on-prem-integration": "skills/ping-app-integration/references/generated/on-prem-integration/.gitkeep" + } + }, + "ping-identity-for-ai": { + "curated": [ + "skills/ping-identity-for-ai/references/curated/identity-for-ai-overview.md", + "skills/ping-identity-for-ai/references/curated/verified-trust-overview.md", + "skills/ping-identity-for-ai/references/curated/agent-security-patterns.md", + "skills/ping-identity-for-ai/references/curated/workforce-helpdesk-ai.md", + "skills/ping-identity-for-ai/references/curated/agent-gateway-mcp.md" + ], + "generated": {} + } + } +} diff --git a/plugins/ping-identity/routing-hints.md b/plugins/ping-identity/routing-hints.md new file mode 100644 index 0000000..708bdfe --- /dev/null +++ b/plugins/ping-identity/routing-hints.md @@ -0,0 +1,42 @@ +# Routing Hints — Ping Identity Plugin + +Lightweight routing rules for plugin-only installs. Replaces `/shared/taxonomies/routing-rules.md` when the full repo is absent. + +## Step 1 — Select the skill + +| User intent | Skill | +|---|---| +| "Where do I start?", "Which product?", "Help me choose" | `ping-quickstart` | +| "Set up", "configure", "admin", "create environment/tenant", "add app", "manage directory", "install" | `ping-foundation` | +| "Build a flow", "DaVinci", "journey", "auth tree", "orchestrate" | `ping-orchestration` | +| "AI agent identity", "Verified Trust", "identity for AI" | `ping-identity-for-ai` | +| "Use Protect", "Verify", "IGA", "Credentials", "Authorize", "Neo" | `ping-universal-services` | +| "Integrate my app", "SDK", "mobile", "React", "iOS", "Android", "web login" | `ping-app-integration` | + +## Step 2 — Detect the platform + +| Signal | Platform tag | +|---|---| +| "PingOne", "apps.pingone.com", PingOne admin console | `pingone-mt` | +| "AIC", "PingOne ST", "PingAM", "IDM", "ForgeRock", "Identity Management", "IGA" | `pingone-st` | +| "PingFederate", "PingAccess", "PingDirectory", "PingAM", "PingIDM", "PingDirectory", "PingAuthorize", "PingDS", PingGateway", "software", "on-prem", "self-managed" | `ping-software` | +| Service question spanning PingOne MT and PingOne ST | `cross-platform` | +| Unknown | Ask: "Are you in PingOne, PingOne ST, or on-premises software?" | + +## Step 3 — Select reference tier (stop at first sufficient tier) + +1. `skills//references/curated/` — load 1–3 anchor docs +2. `skills//references/generated//` — scan shortlist for matching titles + +## Cross-skill escalation + +| Task spans... | Also reference | +|---|---| +| Flow or journey design | `ping-orchestration` SKILL.md | +| Shared service (Protect, Verify, IGA) | `ping-universal-services` SKILL.md | +| App/SDK code | `ping-app-integration` SKILL.md | +| Platform orientation | `ping-quickstart` SKILL.md | + +## Principle + +Use the smallest context first. Only widen retrieval when the current tier cannot complete the task. diff --git a/plugins/ping-identity/skills/ping-app-integration/SKILL.md b/plugins/ping-identity/skills/ping-app-integration/SKILL.md new file mode 100644 index 0000000..b7996af --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/SKILL.md @@ -0,0 +1,89 @@ +--- +name: ping-app-integration +description: Implementation skill for integrating Ping Identity into web, mobile, and SDK experiences. Use this whenever a task involves Android, iOS, or React SDK integration; embedding journeys or DaVinci flows into an application; wiring OIDC / OAuth redirect flows; browser-based auth flows; orchestration SDK references; on-prem app-side integration; migrating from the ForgeRock SDK to the Ping SDK; or troubleshooting app-side errors such as redirect_uri_mismatch, CORS errors on the token endpoint, token refresh failures, or push MFA not delivering. Also use when the task is integrating a Ping service SDK (e.g., Protect JavaScript SDK, Verify SDK) into app code. Also invoke with /ping-app-integration. +compatibility: Designed for Ping Identity app and SDK integration work. References product docs and SDK documentation. +metadata: + publisher: Ping Identity + version: "0.2.0" +--- + +# ping-app-integration + +Implementation skill for integrating Ping Identity into web, mobile, and application SDK experiences. + +## Invocation + +Invoke explicitly with `/ping-app-integration` or by saying "use ping-app-integration to...". + +## When to use this skill + +- "Integrate Ping into my React app using the orchestration SDK" +- "Use the iOS SDK with AIC journeys" +- "Wire OIDC redirect into a mobile app" +- "Embed a journey in a webview" +- "Migrate from the ForgeRock SDK to the Ping SDK" +- "Add browser-based login to my web application" +- "Connect my Android app to a DaVinci flow" +- "App-side OIDC / OAuth configuration for PingFederate" +- "Redirect URI mismatch error in my app" +- "CORS error on the token endpoint" + +## When NOT to use this skill + +- Platform setup or app registration (no code): use `ping-foundation`. +- Designing the flow or journey itself: use `ping-orchestration`. +- General onboarding orientation: use `ping-quickstart`. + +## Multi-skill use cases + +A complete app integration spans three layers — all are required for a production outcome: + +| Layer | Skill | Typical tasks | +|---|---|---| +| 1. Platform setup + app registration | `ping-foundation` | Tenant setup, redirect URI registration, sign-on policy | +| 2. Journey or flow design | `ping-orchestration` | Journey nodes, DaVinci flows, MFA policy | +| 3. SDK / app-side implementation | `ping-app-integration` (this skill) | SDK install, init, auth flow, token storage | + +Complete platform setup first, then flow design, then hand off to this skill for SDK wiring. + +**End-to-end example (Android + DaVinci on PingOne MT):** + +1. Use `ping-foundation` to register an OIDC application in PingOne MT, configure the redirect URI (`myapp://callback`), and note the client ID and environment ID. +2. Use `ping-orchestration` to build and test a DaVinci login flow with username/password and push MFA nodes. +3. Use this skill (`ping-app-integration`) to add the `com.pingidentity.sdks:davinci` and `com.pingidentity.sdks:oidc` dependencies, initialize `PingOne.init(context)` with the client ID and discovery endpoint, call `PingOne.startAuthentication(activity)`, and handle the `AuthResult`. + +## Step 1: What are you trying to do? + +| Task | Curated reference | +|---|---| +| Overview: skill positioning, SDK landscape, integration lifecycle | `references/curated/app-integration-overview.md` | +| Android SDK integration (Journey or DaVinci) | `references/curated/mobile-integration-basics.md` | +| iOS Swift SDK integration (Journey or DaVinci) | `references/curated/mobile-integration-basics.md` | +| React / JavaScript web integration | `references/curated/web-integration-basics.md` | +| Generic OIDC web app or SAML integration | `references/curated/web-integration-basics.md` | +| Browser-based redirect / hosted login | `references/curated/web-integration-basics.md` | +| On-prem app integration (PingFederate, PingAccess) | `references/curated/web-integration-basics.md` | +| Server-side / backend OIDC, M2M client_credentials, token exchange, CIBA, retry/429 | `references/curated/server-side-integration-basics.md` | +| Troubleshooting, migration (ForgeRock → Ping SDK) | `references/curated/integration-troubleshooting-basics.md` | + +## Step 2: Retrieval escalation + +1. Curated anchors (`references/curated/`) — load 1–3 max. Stop if sufficient. +2. Generated shortlists (`references/generated/web/`, `references/generated/mobile/`, `references/generated/on-prem-integration/`, `references/generated/orchestration-sdks/`) — load the relevant directory for the surface; skip if empty. +3. Docs MCP fallback — see `references/runtime/docs-mcp-routing.md`. Only if curated + shortlist insufficient. + +## Companion SDK skills — `ping-sdk-agent-skills` + +For deep implementation work (full code scaffolding, collector rendering, migration automation), delegate to the specialist skills in the `pingidentity/ping-sdk-agent-skills` plugin: + +| Specialist skill | Platform | +|---|---| +| `ping-orchestration-sdk-router` | Detects platform (Android/iOS/JS) and routes — use first when platform is ambiguous | +| `ping-orchestration-android-sdk` | Android Kotlin / Jetpack Compose — Journey + DaVinci | +| `ping-orchestration-ios-sdk` | iOS Swift / SwiftUI — Journey + DaVinci | +| `ping-orchestration-reactjs-journey-sdk` | React + AIC Journey flows | +| `ping-orchestration-reactjs-davinci-sdk` | React + PingOne MT DaVinci flows | +| `ping-orchestration-javascript-sdk` | Angular / Vue / Vanilla JS | +| `forgerock-to-ping-journey-migration` | ForgeRock SDK → Ping SDK automated migration | + +Install: `https://github.com/pingidentity/ping-sdk-agent-skills` diff --git a/plugins/ping-identity/skills/ping-app-integration/ping-marketplace.json b/plugins/ping-identity/skills/ping-app-integration/ping-marketplace.json new file mode 100644 index 0000000..b064201 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/ping-marketplace.json @@ -0,0 +1,33 @@ +{ + "skill_id": "ping-app-integration", + "display_name": "Ping Identity App Integration", + "description": "Web, mobile, and SDK integration patterns for Ping Identity platforms. Covers Android, iOS, and React SDK references; OIDC web app integration; browser auth flows; orchestration SDK references; and on-prem app-side integration.", + "version": "0.2.0", + "publisher": "Ping Identity", + "tags": { + "product_family": ["pingone-mt", "pingone-st", "ping-software", "cross-platform"], + "products": [ + "pingone", + "pingone-st", + "pingfederate", + "pingaccess" + ], + "capabilities": ["app-integration"], + "audience": ["developer"], + "use_cases": ["workforce", "customer"], + "protocols": ["oidc", "oauth2", "saml"] + }, + "entry_point": "SKILL.md", + "references": { + "curated_path": "references/curated/", + "generated_path": "references/generated/" + }, + "related_skills": [ + "ping-quickstart", + "ping-foundation", + "ping-orchestration" + ], + "min_context_tokens": 500, + "max_curated_docs": 3, + "max_shortlist_docs": 20 +} diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/curated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/app-integration-overview.md b/plugins/ping-identity/skills/ping-app-integration/references/curated/app-integration-overview.md new file mode 100644 index 0000000..8ed236c --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/curated/app-integration-overview.md @@ -0,0 +1,189 @@ +--- +title: "Ping Identity App Integration — Overview" +product_family: cross-platform +products: ["pingone", "pingone-aic", "pingfederate", "pingaccess", "pingdirectory"] +capabilities: ["app-integration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["customer", "workforce", "cross-platform"] +doc_type: concept +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://developer.pingidentity.com/pingone-api/platform/" +--- + +# Ping Identity App Integration — Overview + +Orientation for developers choosing the correct SDK surface and integration pattern for embedding Ping Identity authentication into a web, mobile, or on-prem application. + +## Scope + +**Covers:** +- When to use this skill vs. `ping-foundation` (implementation vs. admin) +- SDK landscape: Android, iOS, JavaScript/React, DaVinci orchestration SDK, on-prem agents +- Integration lifecycle: setup → flow design → app-side wiring +- Routing table: developer task → SDK/surface → reference anchor + +**Does NOT cover:** +- Platform-level setup, application registration, or tenant configuration — use `ping-foundation` +- Designing or building journeys and DaVinci flows — use `ping-orchestration` +- Universal services (Protect, Verify, Credentials) invocation patterns — use `ping-universal-services` +- Generic onboarding orientation — use `ping-quickstart` + +## Skill positioning: implementation vs. administration + +`ping-app-integration` covers the **developer's side** of a Ping Identity integration. It answers: "How do I write code that authenticates users through Ping?" + +`ping-foundation` covers the **admin's side**: tenant setup, application records, custom domains, policies. An agent must complete those steps first (or in parallel) before the developer-side wiring can succeed. + +A correct integration always requires both layers: + +| Layer | Primary skill | Typical tasks | +|---|---|---| +| 1. Platform setup | `ping-foundation` | Create tenant, register OIDC application, configure sign-on policy, set redirect URIs | +| 2. Flow / journey design | `ping-orchestration` | Author Journey nodes, build DaVinci flows, configure MFA policies | +| 3. App-side wiring | `ping-app-integration` | Install SDK, initialize client, wire auth code flow, handle callbacks/collectors, store tokens | + +When a user says "add Ping login to my app," the correct sequence is: complete platform setup and flow design first, then hand off to this skill for the SDK wiring. + +## SDK landscape + +### Android — PingOne Native SDK (Kotlin) + +Gradle artifact: `com.pingidentity.sdks:android` + +| Module | Purpose | +|---|---| +| `journey` | Renders Journey callbacks (Username/Password, OTP, Push, Biometric) | +| `davinci` | Drives DaVinci collectors in Jetpack Compose | +| `oidc` | Authorization code + PKCE, token management | +| `fido` | FIDO2 / passkey registration and assertion | +| `protect` | PingOne Protect signals collection | +| `externalidp` | Social login (Google, Apple, Facebook) via IdP-initiated flow | +| `binding` | Device binding and integrity attestation | +| `oath` | TOTP / HOTP soft-token generation | +| `push` | Push notification–based MFA (FCM/APNs) | + +Reference: `references/curated/mobile-integration-basics.md` + +### iOS — PingOne Native SDK (Swift) + +Swift Package: `Ping/ping-ios-sdk` + +| Module | Purpose | +|---|---| +| `PingJourney` | Journey callbacks in SwiftUI / UIKit | +| `PingDavinci` | DaVinci collectors in SwiftUI | +| `PingOidc` | OIDC authorization code + PKCE, token management | +| `PingExternalIdP` | Social login via IdP-initiated flow | +| `PingProtect` | PingOne Protect signals | +| `PingOath` | TOTP / HOTP soft-token generation | +| `PingLogger` | Structured debug logging | +| `PingStorage` | Keychain-backed secure token storage | + +Reference: `references/curated/mobile-integration-basics.md` + +### Web / JavaScript — Ping Orchestration JavaScript SDK + +npm packages: + +| Package | Purpose | +|---|---| +| `@forgerock/journey-client` | Journey callback rendering for web apps | +| `@forgerock/davinci-client` | DaVinci collector rendering for SPAs | +| `@forgerock/oidc-client` | OIDC token exchange, session management, silent renewal | + +Supports React (stable), Angular, Vue, and vanilla JS. + +Reference: `references/curated/web-integration-basics.md` + +### DaVinci Orchestration SDK (cross-platform) + +DaVinci flows are orchestrated server-side; the client SDK renders collectors. The same DaVinci flow definition works with Android, iOS, and JavaScript clients — only the rendering layer differs. + +Routing rule: if the user asks about designing the flow → `ping-orchestration`; if they ask about rendering collectors in their app → `ping-app-integration`. + +### On-prem agents — PingFederate / PingAccess + +| Agent type | Use case | +|---|---| +| PingFederate Java Integration Kit | Add SSO/federation to Java EE apps | +| PingAccess Agent SDK | Policy enforcement point for web apps behind PingAccess | +| PingFederate OAuth AS integration | Existing apps adopting OAuth2/OIDC via PF as the AS | +| PingFederate SAML SP connector | SAML SP-initiated SSO from third-party apps | + +On-prem agents are configured at the PingFederate/PingAccess level; app-side changes are typically limited to redirect URI handling and token validation. + +## Developer task → reference routing table + +| Developer task | SDK / surface | Curated reference | +|---|---|---| +| Android Journey or DaVinci integration | Android SDK | `references/curated/mobile-integration-basics.md` | +| iOS Swift Journey or DaVinci integration | iOS SDK | `references/curated/mobile-integration-basics.md` | +| React / JavaScript OIDC web app | JS SDK / generic OIDC | `references/curated/web-integration-basics.md` | +| Browser-based hosted login (redirect) | Generic OIDC + browser flows | `references/curated/web-integration-basics.md` | +| SAML SP integration (web app) | SAML (PingFederate / PingOne) | `references/curated/web-integration-basics.md` | +| Redirect URI mismatch, CORS errors, token failures | All surfaces | `references/curated/integration-troubleshooting-basics.md` | +| ForgeRock → Ping SDK migration (Android/iOS/JS) | All SDK surfaces | `references/curated/integration-troubleshooting-basics.md` (migration section) | +| Platform setup / app registration | Admin surface | `ping-foundation` skill | +| Flow or journey design | DaVinci / Journey | `ping-orchestration` skill | + +## Integration lifecycle + +A production-ready integration passes through three phases. This skill owns Phase 3. + +### Phase 1 — Platform setup (ping-foundation) + +- Tenant provisioning (PingOne MT, AIC, or PF license install) +- Application record creation (OIDC client ID, client secret, redirect URIs) +- Sign-on policy / authentication policy attachment +- Custom domain and certificate configuration +- User population or directory integration + +**Handoff point:** Phase 1 is done when the application has a stable client ID, at least one redirect URI registered, and a policy attached. + +### Phase 2 — Flow design (ping-orchestration) + +- Journey node graph authored (AIC / PingAM) +- DaVinci flow logic defined (PingOne MT) +- MFA policies and risk signal routing configured +- Callbacks / collectors identified for the app to render + +**Handoff point:** Phase 2 is done when the flow runs end-to-end in the admin preview and produces the expected token. + +### Phase 3 — App-side wiring (ping-app-integration — this skill) + +- SDK installed (Gradle / SPM / npm) +- SDK initialized with client ID, redirect URI, and scopes +- Auth code + PKCE flow wired to the login entry point +- Callbacks / collectors rendered in the app UI +- Tokens received and stored in platform-secure storage +- Session refresh and logout implemented + +## Prerequisites + +- An application record exists in the Ping platform (client ID and redirect URI registered) +- The flow or journey is functional (tested in the admin preview) +- Target platform SDK version requirements: Android API 23+, iOS 14+, Node.js 16+ for JS +- Network access from the app to the Ping AS token endpoint (no corporate proxy blocking) + +## Common variants + +| Variant | Note | +|---|---| +| PingOne MT (multi-tenant cloud) | Use `@forgerock/davinci-client` or Android/iOS DaVinci modules; flows are DaVinci-based | +| PingOne AIC / ST (single-tenant) | Use `@forgerock/journey-client` or Android/iOS Journey modules; flows are Journey-based | +| PingFederate on-prem | OIDC or SAML from PF; no native Ping SDK needed unless also using MFA; agent-based enforcement via PingAccess | +| ForgeRock SDK (legacy) | Being replaced by Ping native SDKs; migration guide in `references/curated/integration-troubleshooting-basics.md` | + +## Related references + +- `references/curated/mobile-integration-basics.md` — Android and iOS SDK wiring +- `references/curated/web-integration-basics.md` — JavaScript / React / OIDC / SAML web integration +- `references/curated/integration-troubleshooting-basics.md` — Failure modes and migration path +- `references/runtime/docs-mcp-routing.md` — Escalation to Docs MCP when curated content is insufficient + +## Source + +[Ping Identity Developer Documentation](https://developer.pingidentity.com/pingone-api/platform/) diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/integration-troubleshooting-basics.md b/plugins/ping-identity/skills/ping-app-integration/references/curated/integration-troubleshooting-basics.md new file mode 100644 index 0000000..cd906d4 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/curated/integration-troubleshooting-basics.md @@ -0,0 +1,240 @@ +--- +title: "Integration Troubleshooting — Top Failure Modes" +product_family: cross-platform +products: ["pingone", "pingone-aic", "pingfederate"] +capabilities: ["app-integration"] +services: [] +audience: ["developer"] +use_cases: ["customer", "workforce", "cross-platform"] +doc_type: troubleshooting +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/troubleshooting/p1_troubleshoot_apps.html" +--- + +# Integration Troubleshooting — Top Failure Modes + +Diagnostic guide for the most common failure patterns in Ping Identity app integrations — covering redirect URI mismatch, CORS errors, token failures, push MFA, and ForgeRock → Ping SDK migration breaking changes. + +## Scope + +**Covers:** +- Redirect URI mismatch — diagnosis and fix +- CORS errors on the token endpoint — cause, diagnosis, fix +- Token introspection failures — invalid token, clock skew, expiry +- Refresh token not honored — scope omission +- Push MFA not delivered — FCM/APNs configuration and device registration +- ForgeRock SDK → Ping SDK migration breaking changes (renamed packages, callback model, init API) +- Quick-reference diagnostic table: symptom → likely cause → fix + +**Does NOT cover:** +- SDK installation or initialization steps — see `references/curated/mobile-integration-basics.md` or `references/curated/web-integration-basics.md` +- Journey node or DaVinci flow authoring errors — use `ping-orchestration` +- Platform-side application configuration — use `ping-foundation` + +## Failure mode 1: Redirect URI mismatch + +**Error messages:** `redirect_uri_mismatch`, `invalid_request: redirect_uri does not match`, `error=invalid_request` + +**Cause:** The authorization server performs an exact-match comparison between the `redirect_uri` value in the request and the set of URIs registered for the application. Any character difference results in rejection. + +**Common mismatch patterns:** + +| Mismatch type | Example registered | Example sent by app | Outcome | +|---|---|---|---| +| Trailing slash | `https://app.example.com/callback` | `https://app.example.com/callback/` | Rejected | +| Protocol case | `https://app.example.com/callback` | `HTTPS://app.example.com/callback` | Rejected | +| http vs https | `https://app.example.com/callback` | `http://app.example.com/callback` | Rejected | +| Extra query param | `https://app.example.com/callback` | `https://app.example.com/callback?env=prod` | Rejected | +| Scheme mismatch | `myapp://callback` | `myapp://callback/` | Rejected | +| Localhost port | `http://localhost:3000/callback` | `http://localhost:3001/callback` | Rejected | + +**Diagnosis:** Capture the full authorization request URL (browser network tab or SDK debug log). Extract the `redirect_uri` parameter value. Compare byte-for-byte against the list of registered URIs in the application record. Admin surface: Applications → [app] → Redirect URIs. + +**Fix:** Update either the registered URI in the admin console (add the exact value the app sends) or update the app to send the registered value exactly. Register all environment variants (dev, staging, prod) as separate entries — wildcard URIs are not supported by most Ping platform configurations. + +## Failure mode 2: CORS errors on the token endpoint + +**Symptom:** Authorization request succeeds (browser redirects to login page, user authenticates, redirect back occurs), but the token exchange fails with a network error in the browser console. No HTTP response body is visible. The error in the console typically reads: `Access to XMLHttpRequest at 'https:///token' from origin 'https://app.example.com' has been blocked by CORS policy`. + +**Cause:** The browser is performing a cross-origin POST to the token endpoint. The Ping authorization server must include `Access-Control-Allow-Origin: https://app.example.com` (or `*` for public endpoints) in the response headers. + +**Diagnosis:** Inspect the network traffic to the token endpoint (`POST /token`). Check whether an OPTIONS preflight request is present; if it receives a 4xx response or is missing `Access-Control-Allow-Origin` in the response headers, the problem is server-side CORS configuration. If no OPTIONS preflight is present, the browser is not issuing one — verify that the token request uses `Content-Type: application/x-www-form-urlencoded` (correct) rather than `application/json` (triggers a preflight and is often misconfigured). If both preflight and actual request are present but `Access-Control-Allow-Origin` is absent from the response headers, server-side CORS configuration is incomplete. + +**Fix by platform:** + +| Platform | Fix | +|---|---| +| PingOne MT | CORS origins are automatically configured for registered redirect URI origins — verify the app's origin matches a registered redirect URI's origin (`scheme://host:port`) | +| AIC / PingOne ST | Same as PingOne MT; also check if the OAuth2 provider CORS configuration is set in the AM admin console | +| PingFederate | Enable CORS in `pf.properties`: `pf.cors.enabled=true`; add allowed origins to the CORS filter configuration in the PF admin console | + +**Architectural alternative:** Move the token exchange to a server-side BFF (Backend For Frontend). The browser makes a same-origin request to the BFF, which performs the token exchange server-to-server. Eliminates CORS entirely for the token endpoint. + +## Failure mode 3: Token introspection failures + +**Error conditions:** `invalid_token`, `token_inactive`, `401 Unauthorized` at a resource server + +**Sub-case A — Invalid or malformed token:** + +Introspect the token using the `/introspect` endpoint: + +``` +POST /introspect +Authorization: Basic +Content-Type: application/x-www-form-urlencoded + +token= +``` + +Response `{"active": false}` confirms the token is invalid. Use `jwt.io` or `token.dev` to decode the JWT and inspect the payload for expiry (`exp`) and audience (`aud`). + +**Sub-case B — Clock skew (> 5 seconds):** + +The authorization server sets `iat` and `exp` using its system clock. The resource server validates `exp` against its own clock. A skew greater than 5 seconds causes premature expiry rejections. + +Diagnostic: compare `exp` in the token to the resource server's current time (`date +%s`). A difference of 5+ seconds indicates clock skew. + +Fix: synchronize both systems to an NTP server. Configure a clock skew tolerance on the resource server (`allowed_clock_skew` in Spring Security, `clockSkew` in passport-jwt, etc.) as a temporary mitigation — the root cause is always the unsynchronized clock. + +**Sub-case C — Expired token:** + +`exp` is in the past. The app should have used the refresh token to obtain a new access token before expiry. If the refresh token itself has expired, a full re-authentication is required. + +Check: does the app perform proactive token refresh (e.g., 60 seconds before `exp`) or only reactive refresh (after receiving a 401)? Reactive refresh is riskier under high latency — prefer proactive. + +**Sub-case D — Audience mismatch:** + +The access token's `aud` claim does not include the resource server's identifier. The resource server rejects the token. + +Fix: ensure the `audience` parameter is set correctly in the authorization request or the resource server identifier matches the configured audience in the Ping application or OAuth2 policy. + +## Failure mode 4: Refresh token not honored + +**Symptom:** The app presents a refresh token to the token endpoint with `grant_type=refresh_token` and receives `invalid_grant` or the refresh token is ignored (no `refresh_token` in the token response). + +**Cause A — `offline_access` scope not requested:** + +Refresh tokens are only issued when the `offline_access` scope is included in the authorization request. This scope is commonly omitted. + +Verification: decode the access token or ID token and check the `scope` claim. If `offline_access` is absent, the authorization server will not issue a refresh token. + +Fix: add `offline_access` to the `scope` parameter in the initial authorization request. + +**Cause B — Refresh token reuse policy:** + +PingOne and PingFederate support refresh token rotation. After one use, the original refresh token is invalidated. If the app stores and reuses the original token after a successful rotation, subsequent refreshes fail with `invalid_grant`. + +Fix: always store the new refresh token returned in the token response and discard the old one. + +**Cause C — Refresh token lifetime exceeded:** + +The refresh token has an absolute lifetime (typically 30–90 days, configurable per application in the admin console). After expiry, a full re-authentication is required. + +## Failure mode 5: Push MFA not delivered + +**Symptom:** User completes password authentication, push notification should arrive on the registered device, but the notification never appears. The journey or DaVinci flow times out waiting for push approval. + +**Android (FCM) diagnostic checklist:** + +1. `google-services.json` is present in the app module and references the correct Firebase project +2. `PingOne.registerDevice(fcmToken)` was called with a valid FCM token after the user authenticated +3. The FCM token is current — FCM tokens rotate; call `FirebaseMessaging.getInstance().token` at app startup and re-register if the token changes +4. The Firebase project is configured in the PingOne admin console: Applications → [app] → Push Notifications → FCM Server Key +5. Verify device registration: admin surface — Users → [user] → Devices; a registered device appears with status "registered". + +**iOS (APNs) diagnostic checklist:** + +1. APNs certificate or APNs auth key is configured in the Ping admin console +2. App requests push notification permission (`UNUserNotificationCenter.requestAuthorization()`) and calls `UIApplication.registerForRemoteNotifications()` +3. The APNs device token is passed to the Ping SDK: `PingOne.setAPNSDeviceToken(deviceToken)` in `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` +4. Push notification entitlement is present in the app's entitlements file: `aps-environment = development` (dev) or `aps-environment = production` (release) +5. APNs sandbox vs. production: use sandbox for debug builds, production for TestFlight and App Store + +**Common root cause:** Device registration step is skipped. The device must be registered before push notifications can be sent. Registration associates the device's push token with the user account in the Ping platform. + +## Failure mode 6: ForgeRock → Ping SDK migration breaking changes + +Migrating from `forgerock-android-sdk` or `forgerock-ios-sdk` to the Ping Native SDKs requires addressing the following breaking changes: + +### Android breaking changes + +| Area | ForgeRock SDK | Ping SDK | Action | +|---|---|---|---| +| Gradle group ID | `org.forgerock:forgerock-android-sdk` | `com.pingidentity.sdks:android` | Update all `implementation()` declarations | +| Initialization method | `FRAuth.start(context)` | `PingOne.init(context) { ... }` DSL | Replace init call; move config to builder DSL | +| Login flow entry point | `FRUser.login(context, callbacks)` | `PingOne.startAuthentication(activity)` | Replace call site | +| Node type | `FRNode` | `Node` (sealed class) | Update type references and `when` branches | +| Callback sealed types | `FRCallback` subclasses | `Callback` sealed subclasses (renamed) | Audit `instanceof` / `is` checks — names changed | +| Token retrieval | `FRUser.getCurrentUser()?.getAccessToken()` | `tokens.accessToken` from `AuthResult.Success` | Update access pattern | +| Session token | `FRSession.getCurrentSession()` | `PingOne.getSession()` | Update session checks | + +### iOS breaking changes + +| Area | ForgeRock SDK | Ping SDK | Action | +|---|---|---|---| +| Package URL | `github.com/ForgeRock/forgerock-ios-sdk` | `github.com/ForgeRock/ping-ios-sdk` | Update SPM dependency URL | +| Module naming | `FRAuth`, `FRCore`, `FRProximity` | `PingOidc`, `PingJourney`, `PingStorage` | Update imports | +| Initialization | `FRAuth.start()` (static) | `OidcClient(config:)` instance | Replace with instance-based init | +| Node / callback model | `FRNode`, `FRCallback` | `Node`, typed `Callback` protocol | Update protocol conformances | +| Token storage | `FRUser.currentUser` tokens | `PingStorage` / `Keychain`-backed `TokenManager` | Update token access patterns | +| Journey entry | `FRUser.login(completion:)` | `journeyClient.start()` async | Convert to async/await | + +### JavaScript breaking changes + +| Area | ForgeRock SDK | Ping SDK | Action | +|---|---|---|---| +| Package name | `@forgerock/javascript-sdk` | `@forgerock/journey-client` or `@forgerock/davinci-client` | Update `package.json` dependencies | +| Config entry | `Config.set({...})` | Same API (no change) | No action needed | +| FRAuth node iteration | `FRAuth.next(previousStep, {realmPath})` | `journeyClient.next(step)` | Update iteration calls | +| Token retrieval | `TokenManager.getTokens()` | `@forgerock/oidc-client` `getTokens()` | Update import + call | +| Session management | `FRSession.logout()` | `oidcClient.logout()` | Update logout call | + +**Migration strategy — manual approach:** address breaking changes in order — (1) dependency declarations, (2) imports, (3) initialization, (4) flow entry points, (5) callback/node handling, (6) token retrieval and storage. Test each phase with the existing journey or DaVinci flow before proceeding to the next. + +**Automated migration:** For full automated code migration, use the `forgerock-to-ping-journey-migration` skill from the `pingidentity/ping-sdk-agent-skills` plugin. That skill runs a structured 9-phase workflow: (1) Detect platform, (2) Gather context (read manifest, package.json, Podfile), (3) Pre-flight build check, (4) Scope migration, (5) Scan for legacy usage, (6) Preview plan, (7) Apply changes with `[ping-migration] BEGIN/END legacy` comment markers, (8) Post-flight build verification, (9) Write migration report. It never silently deletes code and keeps the build working at every step. + +## Quick-reference diagnostic table + +| Symptom | Likely cause | Fix | +|---|---|---| +| `redirect_uri_mismatch` at authorization | App sends URI that does not exactly match registered URI | Compare byte-for-byte; register the exact URI the app sends | +| Network error on token exchange (CORS) | Token endpoint does not allow the app's origin | Configure CORS allow-origins on PingOne/PF; or use BFF pattern | +| Token introspection returns `{"active": false}` | Token expired, invalid, or wrong audience | Check `exp`, `aud` in JWT; verify clock sync; request correct audience | +| `invalid_grant` on refresh | `offline_access` scope missing; refresh token rotated but old token reused | Add `offline_access`; store and use the new refresh token after rotation | +| Push MFA not received (Android) | FCM token not registered; FCM key not in admin console | Re-register device; verify FCM server key configuration | +| Push MFA not received (iOS) | APNs token not passed to SDK; sandbox vs. production mismatch | Call `setAPNSDeviceToken` in delegate; check entitlement environment | +| `FRAuth not found` after migration (Android) | Old `org.forgerock` package still imported | Replace all `org.forgerock` imports with `com.pingidentity.sdks` | +| `Module 'FRAuth' not found` (iOS) | Old SPM URL still in Package.swift | Update package URL to `github.com/ForgeRock/ping-ios-sdk` | +| `@forgerock/javascript-sdk` import error (JS) | Package removed; new package not installed | `npm install @forgerock/journey-client @forgerock/oidc-client` | +| No `refresh_token` in token response | `offline_access` not in requested scopes | Add `offline_access` to `scope` parameter | +| 401 on API call with valid-looking token | Clock skew > 5s; audience mismatch | Sync clocks; verify `aud` claim matches resource server identifier | +| Silent renewal fails (Safari/Chrome) | Third-party cookie restrictions block iframe session | Use full-page redirect for renewal; or BFF session pattern | + +## Prerequisites + +- SDK version: verify the installed SDK version meets minimum requirements (Android SDK ≥ 3.0, iOS SDK ≥ 3.0, JS SDK ≥ 4.0 for Ping Native SDKs) +- Application record with registered redirect URI and correct scopes (use `ping-foundation` to verify) +- For push MFA: FCM/APNs credentials configured in the Ping admin console +- Access to admin console to verify registered URIs, CORS origins, and device registrations + +## Common variants + +| Variant | Note | +|---|---| +| PingFederate on-prem | CORS configuration is in `pf.properties` + admin console; not auto-configured from redirect URI | +| AIC (PingOne ST) | Journey-based flows; clock skew issues are most common when the AIC instance is recently provisioned | +| PingOne MT | DaVinci flows; audience claim in access token is the resource server entity configured in the DaVinci policy | +| React Native | Uses the same JS SDK packages as React web; deep link handling differs (see Expo/React Native linking docs) | + +## Related references + +- `references/curated/mobile-integration-basics.md` — Android and iOS SDK setup including migration table +- `references/curated/web-integration-basics.md` — JavaScript OIDC and SAML integration +- `references/curated/app-integration-overview.md` — Integration lifecycle and skill positioning +- `pingidentity/ping-sdk-agent-skills` — `forgerock-to-ping-journey-migration` skill for automated SDK migration + +## Source + +[Ping Identity Troubleshooting Documentation](https://docs.pingidentity.com/pingone/troubleshooting/p1_troubleshoot_apps.html) diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/mobile-integration-basics.md b/plugins/ping-identity/skills/ping-app-integration/references/curated/mobile-integration-basics.md new file mode 100644 index 0000000..eb087e3 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/curated/mobile-integration-basics.md @@ -0,0 +1,303 @@ +--- +title: "Mobile Integration Basics — Android and iOS SDK" +product_family: cross-platform +products: ["pingone", "pingone-aic"] +capabilities: ["app-integration"] +services: [] +audience: ["developer"] +use_cases: ["customer", "workforce", "cross-platform"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/native-sdks/p1_native_sdks_landing.html" +--- + +# Mobile Integration Basics — Android and iOS SDK + +Orientation for integrating Ping Identity authentication into Android (Kotlin) and iOS (Swift) apps using the PingOne Native SDKs — covers both PingOne MT and AIC (PingOne ST) targets. + +## Scope + +**Covers:** +- Android SDK import, initialization, OIDC flow, token storage, deep link handling, MFA push +- iOS SDK import, initialization, OIDC flow, token storage, universal links, Keychain +- Feature comparison table: Android vs. iOS +- Common failure modes for each platform +- Migration path from ForgeRock SDK to Ping Native SDK (naming and API changes) + +**Does NOT cover:** +- Flow or journey design (Journey nodes, DaVinci collectors) — use `ping-orchestration` +- Platform-side application registration or redirect URI setup — use `ping-foundation` +- Web/React SDK integration — see `references/curated/web-integration-basics.md` +- Troubleshooting diagnosis beyond what is listed here — see `references/curated/integration-troubleshooting-basics.md` + +## Android SDK integration + +### SDK import + +Gradle (Kotlin DSL): + +```kotlin +dependencies { + // Core OIDC + Journey + implementation("com.pingidentity.sdks:oidc:") + implementation("com.pingidentity.sdks:journey:") + + // DaVinci (if targeting PingOne MT / DaVinci flows) + implementation("com.pingidentity.sdks:davinci:") + + // Optional modules + implementation("com.pingidentity.sdks:fido:") // FIDO2 / passkeys + implementation("com.pingidentity.sdks:protect:") // Protect signals + implementation("com.pingidentity.sdks:push:") // Push MFA (FCM) + implementation("com.pingidentity.sdks:externalidp:") // Social login +} +``` + +The SDK is hosted on Maven Central and on the Ping Identity Maven repository (`maven.pingidentity.com`). Add both repositories to `settings.gradle.kts` if Maven Central is not resolving the artifact. + +### Initialization + +Initialize once per process lifetime, typically in `Application.onCreate()`: + +```kotlin +PingOne.init(context) { + // Required: OIDC client configuration + oidcConfig { + clientId = "" + discoveryEndpoint = "https:///.well-known/openid-configuration" + // or explicit endpoints: + // authorizationEndpoint = "..." + // tokenEndpoint = "..." + redirectUri = "myapp://callback" + scopes = listOf("openid", "profile", "offline_access") + } + // Optional: custom logger + logger { + level = LogLevel.DEBUG + } +} +``` + +Constraint: `PingOne.init()` must complete before any auth call. Calling `startAuthentication()` before init completes throws `PingOneNotInitializedException`. + +### OIDC authorization code + PKCE flow + +```kotlin +// Trigger login from an Activity or Fragment +val result = PingOne.startAuthentication(activity) + +when (result) { + is AuthResult.Success -> { + val tokens = result.tokens + // tokens.accessToken, tokens.idToken, tokens.refreshToken + } + is AuthResult.Failure -> { + // result.error: PingOneError + } +} +``` + +PKCE is enabled by default in the SDK — no additional configuration required. The SDK generates the `code_verifier` and `code_challenge` internally. + +### Token storage — Android Keystore + +The SDK stores tokens in `EncryptedSharedPreferences` backed by Android Keystore. No plaintext token storage occurs by default. + +Key constraint: the encryption key is tied to the device. Tokens are not portable across devices or backups. Developers must not add `android:allowBackup="true"` for credential data; use `android:fullBackupContent` exclude rules if general backup is needed. + +### Handling redirect URIs (deep links) + +Register the redirect URI as an intent filter in `AndroidManifest.xml`: + +```xml + + + + + + +``` + +The scheme and host must exactly match the `redirectUri` passed to `oidcConfig` and the value registered in the Ping admin console. A mismatch causes `redirect_uri_mismatch` from the authorization server. + +### Push MFA (FCM) + +Requirements: +- FCM project configured in Firebase Console; `google-services.json` present in the app module +- PingOne Push policy enabled on the tenant +- Device registration completed: `PingOne.registerDevice(fcmToken)` called after obtaining an FCM registration token from `FirebaseMessaging.getInstance().token` + +Incoming push messages arrive as FCM data messages. Pass the payload to `PingOne.handleRemoteMessage(remoteMessage)` in `FirebaseMessagingService.onMessageReceived()`. + +Common failure: push not delivered because device registration was skipped. Device registration verification: admin surface — Users → [user] → Devices; a registered device appears with status "registered". + +## iOS SDK integration + +### SDK import (Swift Package Manager) + +In `Package.swift` or the Xcode package dependency dialog: + +``` +https://github.com/ForgeRock/ping-ios-sdk +``` + +Select the modules needed: + +| Module | When to include | +|---|---| +| `PingOidc` | All apps — OIDC token management | +| `PingJourney` | AIC / PingAM Journey-based flows | +| `PingDavinci` | PingOne MT DaVinci-based flows | +| `PingStorage` | Keychain-backed token storage (included by default) | +| `PingExternalIdP` | Social login (Google, Apple, Facebook) | +| `PingProtect` | PingOne Protect risk signals | +| `PingOath` | TOTP / HOTP soft tokens | +| `PingLogger` | Debug and error logging | +| `PingFido` | FIDO2 / passkey registration and assertion (iOS 16+) | + +CocoaPods alternative: `pod 'PingOidc'`, `pod 'PingJourney'`, etc. + +### Initialization + +Initialize in `@main App.init()` or `AppDelegate.application(_:didFinishLaunchingWithOptions:)`: + +```swift +import PingOidc + +let config = OidcClientConfig( + clientId: "", + discoveryEndpoint: "https:///.well-known/openid-configuration", + redirectUri: "myapp://callback", + scopes: ["openid", "profile", "offline_access"] +) +let oidcClient = OidcClient(config: config) +``` + +### OIDC authorization code + PKCE on iOS + +```swift +import PingOidc + +let result = await oidcClient.authorize() + +switch result { +case .success(let tokens): + // tokens.accessToken, tokens.idToken, tokens.refreshToken +case .failure(let error): + // PingOidcError +} +``` + +PKCE is generated by the SDK. The authorization flow launches `ASWebAuthenticationSession` internally when using the redirect-based flow. + +### Token storage — Keychain + +`PingStorage` persists tokens in the iOS Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`. This attribute ties the token to the device and excludes it from iCloud Keychain sync. + +Key constraint: tokens are unavailable while the device is locked (screen off). Apps that require background token refresh must use `kSecAttrAccessibleAfterFirstUnlock`; this requires a custom `StorageConfiguration` passed to `OidcClientConfig`. + +### Universal links for redirect URI + +For a redirect URI of the form `https://app.example.com/callback`: +- An Apple App Site Association (AASA) file must be hosted at `https://app.example.com/.well-known/apple-app-site-association` +- The app must have the Associated Domains entitlement: `applinks:app.example.com` +- `onOpenURL` (SwiftUI) or `application(_:continue:restorationHandler:)` (UIKit) must forward the URL to `oidcClient.handleRedirect(url:)` + +For custom-scheme URIs (`myapp://callback`): no AASA file required; register the scheme in `Info.plist` under `CFBundleURLTypes`. + +### Biometric authentication + +PingOne push approval and FIDO2 assertions may require biometric prompts. The app needs `NSFaceIDUsageDescription` in `Info.plist` for Face ID. Absence of this key causes a crash at runtime on Face ID–capable devices — it does not degrade gracefully. + +### Swift 6 concurrency notes + +The Ping iOS SDK is compatible with Swift 6's strict concurrency model. Key constraints: + +- All SDK callbacks and `async` methods must be called from the `@MainActor` context or a structured concurrency task. Calling from a background thread without proper actor isolation produces Swift 6 compiler errors. +- `OidcClient` and journey/DaVinci client methods are `async` — use `await` inside `Task { }` blocks when calling from SwiftUI `.onAppear` or button actions. +- The SDK's `@MainActor`-annotated types must not be used from non-isolated closures. If integrating with Combine or legacy UIKit callbacks, use `Task { @MainActor in ... }` to hop to the main actor. + +--- + +## Feature comparison table + +| Feature | Android | iOS | +|---|---|---| +| SDK distribution | Maven Central / Ping Maven repo | Swift Package Manager / CocoaPods | +| Auth flow | `PingOne.startAuthentication(activity)` | `oidcClient.authorize()` async | +| PKCE | Auto-generated by SDK | Auto-generated by SDK | +| Token storage | `EncryptedSharedPreferences` (Keystore) | Keychain (`kSecAttrAccessibleWhenUnlockedThisDeviceOnly`) | +| Redirect URI type | Custom scheme (intent filter) | Custom scheme or universal link | +| Push MFA | FCM (`FirebaseMessagingService`) | APNs (`UNUserNotificationCenter`) | +| Social login | `externalidp` module | `PingExternalIdP` module | +| Biometric | Android Biometric API (via `binding` module) | Face ID / Touch ID (via `NSFaceIDUsageDescription`) | +| FIDO2 / passkeys | `fido` module, Android 9+ | `PingFido` module (iOS 16+, passkey API) | +| DaVinci collectors | `davinci` module, Jetpack Compose | `PingDavinci` module, SwiftUI | +| Journey callbacks | `journey` module | `PingJourney` module | + +## DaVinci collector types (Android and iOS) + +When driving a DaVinci flow with the `davinci` / `PingDavinci` module, the SDK delivers **collectors** per step. Render by type: + +| Collector type | Interaction | Notes | +|---|---|---| +| `TextCollector` | Text input | Username, email, any free-text | +| `PasswordCollector` | Masked password input | | +| `SubmitCollector` | Submit / Continue button | Advances the flow | +| `FlowCollector` | Secondary action button | "Forgot password", "Register" — triggers a sub-flow | +| `SelectCollector` | Dropdown or radio group | | +| `MultiSelectCollector` | Multi-select list | | +| `LabelCollector` | Display-only text | No user input | +| `QrCodeCollector` | QR code display | Flow waits for external device scan | +| `SsoCollector` / `IdpCollector` | Social / external IdP button | Google, Apple, Facebook; triggers IdP redirect | +| `PhoneCollector` | Phone input with country picker | | + +Auto-advancing (no user action): `DeviceAuthenticatorCollector` (biometric/passkey challenge), `ProtectCollector` (Protect signals — silent). + +**Pattern:** iterate `node.collectors`, render by type, collect input, call `node.next()`. + +--- + +## Common failure modes + +| Symptom | Cause | Fix | +|---|---|---| +| `redirect_uri_mismatch` at login | URI sent by app does not match registered URI exactly | Verify scheme, host, and path match precisely — no trailing slash differences | +| Token refresh fails after app background | `offline_access` scope not included; token storage accessibility too strict | Add `offline_access` to scopes; on iOS check `kSecAttrAccessible` setting | +| Biometric auth crash on iOS | `NSFaceIDUsageDescription` missing from `Info.plist` | Add the usage description key | +| Push notifications not received | FCM/APNs not configured; device registration skipped | Complete `PingOne.registerDevice()` / APNs flow; verify FCM project setup | +| Android Keystore error after OS upgrade | Keystore key invalidated post-biometric change | Handle `KeyPermanentlyInvalidatedException`; prompt user to re-authenticate | +| SDK not resolving in Gradle | Ping Maven repo not declared | Add `maven { url = uri("https://maven.pingidentity.com/repository/releases/") }` | + +## Migration path: ForgeRock SDK → Ping SDK + +For full Android, iOS, and JavaScript breaking-change tables and migration strategy, see `references/curated/integration-troubleshooting-basics.md` — Failure mode 6. + +## Prerequisites + +- Application record created in PingOne MT, AIC, or PingAM with the correct redirect URI registered (use `ping-foundation` to complete this step) +- Journey or DaVinci flow designed and tested end-to-end (use `ping-orchestration`) +- Android: API level 23+, Kotlin 1.8+, Gradle 8+ +- iOS: iOS 14+, Swift 5.7+, Xcode 14+ +- For push MFA: FCM project (Android) or APNs certificate/key (iOS) configured in the Ping admin console + +## Common variants + +| Variant | Note | +|---|---| +| PingOne MT target | Use `davinci` / `PingDavinci` modules; flows are DaVinci-based; discovery endpoint is `https://auth.pingone.com//as/.well-known/openid-configuration` | +| AIC / PingOne ST target | Use `journey` / `PingJourney` modules; flows are Journey-based; discovery endpoint is `https://.forgeblocks.com/am/oauth2/realms//.well-known/openid-configuration` | +| PingFederate on-prem | OIDC only (no Journey/DaVinci modules); use `oidc` / `PingOidc` module; discovery endpoint is `https://:/.well-known/openid-configuration` | +| Embedded webview | Not recommended — hosted login in a system browser (Chrome Custom Tab / ASWebAuthenticationSession) is required for security; custom scheme redirect URI must be registered | + +## Related references + +- `references/curated/app-integration-overview.md` — SDK landscape and integration lifecycle +- `references/curated/web-integration-basics.md` — JavaScript and browser-based auth flows +- `references/curated/integration-troubleshooting-basics.md` — Failure mode diagnosis and migration guide + +## Source + +[PingOne Native SDK Documentation](https://docs.pingidentity.com/pingone/native-sdks/p1_native_sdks_landing.html) diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md b/plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md new file mode 100644 index 0000000..aa841b8 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md @@ -0,0 +1,371 @@ +--- +title: "Server-Side App Integration — Backend OIDC and Token Handling" +product_family: cross-platform +products: ["pingone", "pingone-aic", "pingfederate"] +capabilities: ["app-integration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer", "cross-platform"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://developer.pingidentity.com/pingone-api/platform/" +--- + +# Server-Side App Integration — Backend OIDC and Token Handling + +Implementation patterns for backend / server-side applications integrating with Ping Identity: confidential OIDC clients, machine-to-machine auth, token validation, refresh, and resilience under load. Covers Node.js, Java, Python, .NET, and Go using standard OIDC client libraries — not the Ping native SDKs (which target end-user apps). + +## Scope + +**Covers:** +- Confidential OIDC client setup (Authorization Code + PKCE, Client Credentials, Refresh Token) +- Server-side token validation (JWT verification, introspection, signing key rotation) +- Token refresh and silent renewal patterns +- M2M / worker-to-worker auth via Client Credentials +- Backchannel patterns (CIBA, Token Exchange) for delegated access +- Retry, 429 handling, circuit-breaker patterns for token endpoint resilience +- Multi-environment configuration management (Dev / Staging / Prod) + +**Does NOT cover:** +- End-user mobile/web SDK integration — see `references/curated/mobile-integration-basics.md` and `references/curated/web-integration-basics.md` +- Designing the journey/flow that issues the token — see `ping-orchestration` +- Platform-side OAuth client registration — see `ping-foundation` +- AI agent identity patterns (CIBA for HITL, agent-to-agent) — see `ping-identity-for-ai` + +--- + +## Pick the correct grant type + +| Use case | Grant type | Notes | +|---|---|---| +| End-user logs into a web app rendered by your server | Authorization Code + PKCE | Server holds the secret; PKCE adds defense-in-depth | +| End-user logs in via SPA, your backend exchanges/refreshes tokens (BFF pattern) | Authorization Code + PKCE (in BFF) | SPA never sees the secret; BFF handles token storage | +| Backend service calls another backend service on behalf of itself | Client Credentials | No user; opaque or JWT access token | +| Backend service acts on behalf of a user via delegation | Token Exchange (RFC 8693) | Exchange a user token for a downstream-scoped token | +| Backend initiates auth for a user out-of-band (push/SMS to phone) | CIBA (Client Initiated Backchannel Authentication) | Used for IVR, AI agent HITL, transaction approval | +| Constrained device with no browser | Device Authorization Grant | User completes auth on a separate device with a browser | + +**Anti-patterns to avoid:** +- Implicit grant — deprecated; tokens in URL fragment +- ROPC (Resource Owner Password Credentials) — exposes user password to the client; allowed only in legacy migration scenarios +- Refresh tokens stored in browser storage — use HttpOnly cookies via a BFF instead + +--- + +## Library selection + +Use a maintained OIDC-compliant library — do not hand-roll OAuth. + +| Language | Recommended library | Notes | +|---|---|---| +| Node.js / TypeScript | `openid-client` | Spec-compliant; supports discovery, PKCE, token exchange | +| Java | Spring Security OAuth2 / Nimbus OAuth2 SDK | Spring for full apps; Nimbus for libraries | +| Python | `authlib` | Sync + async; supports discovery, PKCE, JWT validation | +| .NET | Microsoft.Identity.Web / IdentityModel | Use IdentityModel for non-Microsoft scenarios | +| Go | `golang.org/x/oauth2` + `github.com/coreos/go-oidc` | Combine for full OIDC flow | + +**OIDC discovery:** Always start from the well-known endpoint: +- PingOne MT: `https://auth.pingone.com/{envId}/as/.well-known/openid-configuration` +- PingOne ST (AIC): `https://.forgerock.io/am/oauth2/realms/root/realms//.well-known/openid-configuration` +- PingFederate: `https://:9031/.well-known/openid-configuration` + +The discovery document defines the AS endpoints and supported algorithms. Do not hardcode token / userinfo / JWKS URLs. + +--- + +## Authorization Code + PKCE pattern (server-rendered web app) + +### Sequence + +``` +Browser → /login on your server + → Server: generate code_verifier, code_challenge, state, nonce + → 302 redirect to AS authorize endpoint with PKCE + state + nonce +Browser → AS hosted login page (or DaVinci flow / AIC journey) + → User authenticates +AS → 302 to your /callback?code=...&state=... + → Server: verify state matches stored value + → Server: POST to AS token endpoint with code + code_verifier + client_secret (or PKCE-only if public) + → Receives id_token, access_token, refresh_token + → Verify id_token signature, issuer, audience, nonce, exp + → Establish server session; set HttpOnly + SameSite=Lax cookie + → Store refresh token server-side (encrypted at rest) + → Redirect to original URL or /home +``` + +### Required server-side checks + +| Check | Why | +|---|---| +| State parameter matches stored value | Prevents CSRF attacks on the redirect | +| Nonce in id_token matches stored value | Prevents replay attacks | +| Issuer matches discovery `issuer` | Token came from the expected AS | +| Audience matches your client_id | Token issued for this app | +| Token expiry not in the past | Token still valid | +| Signature verified against JWKS | Token not tampered with | +| `acr_values` and `auth_time` claims, if requested | Confirms requested authentication context met | + +--- + +## Token validation + +### JWT access token (preferred) + +PingOne supports JWT access tokens. Validate locally — do NOT introspect on every request. + +``` +1. Fetch JWKS from discovery `jwks_uri` (cache for 1 hour; refresh on `kid` mismatch) +2. Parse JWT header, find matching `kid` +3. Verify signature with the corresponding public key +4. Validate claims: iss, aud, exp, nbf, sub +5. Optional: validate `client_id`, `scope`, custom claims +``` + +### Opaque access token + +Some configurations use opaque tokens that require introspection. + +``` +POST {introspection_endpoint} + client_id, client_secret (HTTP Basic) + token= +Response: { active: true|false, sub, scope, exp, ... } +``` + +**Caching:** Cache positive introspection results until a few seconds before `exp`. Cache negative results briefly (1-5s) to absorb scan attempts without flooding the AS. + +### Token rotation and key rollover + +Ping platforms rotate signing keys periodically. Your library MUST: + +- Cache JWKS with a TTL no longer than 1 hour +- On `kid` mismatch, refresh JWKS once before failing +- Tolerate `kid` overlap (multiple keys valid simultaneously) during rollover + +--- + +## Refresh token handling + +Server-side apps SHOULD store refresh tokens encrypted at rest, scoped to the user's session. + +### Pattern — silent renewal + +``` +On API request: + if access_token.exp < now + 60s: + POST token endpoint with grant_type=refresh_token + → receive new access_token (and possibly new refresh_token if rotation enabled) + → atomically replace stored tokens + proceed with API request +``` + +### Refresh token rotation + +When the AS issues a new refresh token on every refresh, treat it as a single-use replacement. + +- Persist the new refresh token before the next request uses it +- If a refresh fails with `invalid_grant`, force the user to re-authenticate (refresh token may have been revoked or already consumed elsewhere) +- Configure a small grace period (`refresh_token_rolling_grace`) on the AS to absorb concurrent requests during rotation + +### Reuse detection + +If the AS rejects an already-used refresh token, the user's tokens may be compromised. Best practice: + +- Log the event with user_id and IP +- Revoke all sessions for the user +- Force re-authentication + +--- + +## Client Credentials (M2M) + +Backend-to-backend without a user. + +``` +POST {token_endpoint} + client_id (public) + client_secret (HTTP Basic) OR private_key_jwt assertion + grant_type=client_credentials + scope= +Response: { access_token, expires_in, token_type, scope } +``` + +**Token caching:** Cache the access token for `expires_in - 60s`. Re-fetch only on expiry. Most clients refresh too aggressively; this hammers the AS. + +**Worker / service principal model:** +- PingOne MT: register a "Worker app"; assign admin roles directly to it +- PingOne ST: create an OAuth 2.0 client with the `client_credentials` grant; use service-account-style scopes +- PingFederate: register an OAuth client with `client_credentials` grant in PingFederate AS + +**Recommendation — `private_key_jwt` over `client_secret_basic`:** asymmetric keys eliminate shared-secret leakage. Generate a key pair, register the public key (or its JWKS URL) with the AS, sign a JWT assertion at request time. + +--- + +## CIBA (Client Initiated Backchannel Authentication) + +For human-in-the-loop or out-of-band auth where the user is not at the same device as the requesting client. + +``` +Backend → AS bc-authorize endpoint + scope, login_hint or login_hint_token, binding_message +Response: auth_req_id, expires_in, interval + +[Backend polls OR receives push notification] + +Backend → AS token endpoint + grant_type=urn:openid:params:grant-type:ciba + auth_req_id= +Response: tokens (when user has approved on their device) +``` + +**Use CIBA for:** +- IVR / call-center step-up: customer approves a transaction on their phone while talking to an agent +- AI agent HITL: agent requests user approval before executing a sensitive action — see `ping-identity-for-ai` +- Server-initiated transaction confirmation + +CIBA support varies by platform. Check `backchannel_authentication_endpoint` in the discovery document. + +--- + +## Token Exchange (RFC 8693) + +Used to swap one token for another with reduced scope, different audience, or different actor context. + +``` +POST {token_endpoint} + grant_type=urn:ietf:params:oauth:grant-type:token-exchange + subject_token= + subject_token_type=urn:ietf:params:oauth:token-type:access_token + audience= + scope= +``` + +**Common patterns:** +- Service A receives a user token, exchanges it for a service-B-scoped token +- AI agent acts on behalf of a user with a delegated, restricted token +- Long-running job exchanges a session token for a long-lived task token + +PingOne MT supports token exchange via the standard token endpoint. Worker apps may need explicit token-exchange enablement at the OAuth provider level. + +--- + +## Resilience patterns + +Token endpoints are critical-path. Backend integrations must handle failures gracefully. + +### 429 (Rate limiting) + +PingOne and AIC apply per-environment rate limits. + +| Header | Action | +|---|---| +| `Retry-After` | Wait the indicated seconds before retry | +| Absent | Use exponential backoff: 1s, 2s, 4s, 8s, max 60s | + +**Do NOT:** spin in a tight retry loop — you will be temporarily blocked. + +### Transient 5xx + +| Code | Cause | Action | +|---|---|---| +| 502 / 503 / 504 | Upstream / AS warming after deploy / network blip | Retry with exponential backoff; max 3 retries | +| 500 | Server error; could be persistent | Retry once; on second failure, fail the request and alert | + +### Circuit breaker + +For high-volume services, wrap the token endpoint in a circuit breaker: + +- Open circuit after N consecutive failures (e.g., 5 in 10s) +- Reject calls fast for cooldown period (e.g., 30s) +- Half-open: allow one trial; on success, close circuit + +This prevents cascading failures when the AS is briefly unavailable. + +### Token caching strategy + +| Scope | TTL | Rationale | +|---|---|---| +| Access token (M2M) | `expires_in - 60s` | Always have a buffer for the request to complete | +| JWKS | 1 hour, refresh on `kid` mismatch | Match Ping rotation cadence | +| Discovery document | 24 hours | Endpoints rarely change; refresh on schema mismatch | +| Introspection result (positive) | until `exp` | Avoid hammering AS | +| Introspection result (negative) | 5s | Limit damage from rejected tokens | + +--- + +## Multi-environment configuration + +Production-grade backends MUST keep environment-specific config out of code: + +| Setting | Source | +|---|---| +| `client_id`, `client_secret` | Env-specific secret store (Vault, AWS Secrets Manager, K8s secrets) | +| `discovery_url` | Env-specific config map | +| `redirect_uri` | Env-specific config; never hardcoded | +| Signing keys (private_key_jwt) | Env-specific KMS / HSM | + +**Per-environment registration:** Register a separate OAuth client per environment (Dev / Staging / Prod). Do NOT share client IDs across environments — a compromised dev secret should not affect production. + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| `redirect_uri` mismatch | `redirect_uri_mismatch` error | Register exact URI; trailing slash + path case matters; register dev/staging/prod separately | +| Clock skew on JWT validation | Random `exp` failures during peak load | Allow ±60s `leeway` on `exp` and `nbf`; sync NTP on all servers | +| JWKS cache too long | After AS key rotation, signature verification fails | TTL ≤ 1 hour; refresh on `kid` miss before failing | +| Scope creep | Request more scopes than needed | Start minimal; add scopes only when a use case requires them | +| ROPC for backend "convenience" | Password leaked to backend; legacy compromise vector | Migrate to Authorization Code or Client Credentials | +| Refresh token in cookie + leak | Token stolen via XSS | Use HttpOnly + SameSite=Strict cookies for refresh tokens; never expose to JS | +| Hardcoded discovery / token endpoints | Breaks when AS hostname changes | Resolve via discovery; cache for 24h | +| `iat` / `nbf` validation rejecting valid tokens | Token issued slightly in future | Apply leeway; check NTP | +| Multiple worker apps for same service | Hard to audit; secrets sprawl | One worker app per service per environment; rotate secrets quarterly | +| 429 retry loop | Service degraded; AS blocking | Honor `Retry-After`; circuit-breaker; never tight-loop | + +--- + +## Prerequisites + +- OAuth client registered in the target Ping environment with the required grant types and redirect URIs (see `ping-foundation`) +- Network access from the backend to the AS token / introspection / JWKS endpoints +- Secret storage for `client_secret` or signing keys (private_key_jwt) +- Time sync (NTP) to keep server clocks within ±60s + +## Common variants + +| Variant | Note | +|---|---| +| BFF (Backend-For-Frontend) | Server holds tokens; SPA receives only HttpOnly session cookie | +| Microservices with mesh | Sidecar proxy validates tokens at the edge; service code reads claims from headers | +| FaaS / serverless | Cache JWKS in module scope to survive cold starts; minimize discovery latency | +| Multi-tenant SaaS | One AS issuer per tenant or shared AS with `tenant` claim; design before scaling | +| Hybrid PingFederate + PingOne | PF as identity bridge; backend trusts PF-issued tokens with PingOne federation | + +## Cross-skill routing + +| If the task is also... | Skill | +|---|---| +| Registering the OAuth client / configuring scopes | `ping-foundation` | +| Designing what the user sees during sign-in | `ping-orchestration` | +| Adding risk evaluation or step-up | `ping-universal-services` | +| AI agent identity / CIBA HITL / token exchange for agent | `ping-identity-for-ai` | +| End-user mobile/web SDK | `references/curated/mobile-integration-basics.md`, `references/curated/web-integration-basics.md` | + +## Related references + +- `references/curated/app-integration-overview.md` +- `references/curated/web-integration-basics.md` +- `references/curated/integration-troubleshooting-basics.md` + +## Source + +- [PingOne OIDC overview](https://docs.pingidentity.com/pingone/openid_connect/p1_oidc_overview.html) +- [PingOne token exchange](https://docs.pingidentity.com/pingone/use_cases/p1_oauth_2_token_exchange.html) +- [PingOne ST OAuth 2.0 guide](https://docs.pingidentity.com/pingoneaic/am-oauth2-guide/oauth2-introduction.html) +- [PingFederate OAuth 2.0](https://docs.pingidentity.com/pingfederate/13.0/administrators_reference_guide/pf_oauth_overview.html) +- [RFC 7636 — PKCE](https://datatracker.ietf.org/doc/html/rfc7636) +- [RFC 8693 — Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693) +- [OpenID CIBA Core](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) diff --git a/plugins/ping-identity/skills/ping-app-integration/references/curated/web-integration-basics.md b/plugins/ping-identity/skills/ping-app-integration/references/curated/web-integration-basics.md new file mode 100644 index 0000000..7ca5342 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/curated/web-integration-basics.md @@ -0,0 +1,305 @@ +--- +title: "Web Integration Basics — React, JavaScript, OIDC, and SAML" +product_family: cross-platform +products: ["pingone", "pingone-aic", "pingfederate"] +capabilities: ["app-integration"] +services: [] +audience: ["developer"] +use_cases: ["customer", "workforce", "cross-platform"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/javascript-sdk/p1_javascript_sdk_landing.html" +--- + +# Web Integration Basics — React, JavaScript, OIDC, and SAML + +Integration guide for web applications authenticating through Ping Identity — covers the Ping JavaScript/React SDK, generic OIDC / OAuth2 flows, SAML SP integration, and browser-specific auth patterns. + +## Scope + +**Covers:** +- Ping JavaScript SDK (`@forgerock/journey-client`, `@forgerock/davinci-client`, `@forgerock/oidc-client`) for React apps +- Generic OIDC authorization code + PKCE for SPAs; client credentials for server-to-server +- OIDC redirect URI exact-match constraint, CORS requirements, token endpoint access +- SAML SP-initiated and IdP-initiated flows — when to use SAML vs. OIDC +- Browser-specific flows: hosted login page redirect, popup/post-message, silent renewal +- Flow type comparison table + +**Does NOT cover:** +- Android or iOS native SDK integration — see `references/curated/mobile-integration-basics.md` +- Journey node or DaVinci flow authoring — use `ping-orchestration` +- Application record creation or redirect URI registration in the admin console — use `ping-foundation` +- Failure-mode diagnosis — see `references/curated/integration-troubleshooting-basics.md` + +## React SDK — Ping JavaScript SDK + +### Package selection + +| Package | Target flow | Supported framework | +|---|---|---| +| `@forgerock/journey-client` | AIC / PingAM Journey-based auth | React (stable); Angular, Vue (roadmap) | +| `@forgerock/davinci-client` | PingOne MT DaVinci-based auth | React (stable); Angular, Vue (roadmap) | +| `@forgerock/oidc-client` | OIDC token lifecycle (any IdP) | Framework-agnostic | + +Install: + +```bash +npm install @forgerock/journey-client @forgerock/oidc-client +# or for DaVinci: +npm install @forgerock/davinci-client @forgerock/oidc-client +``` + +### Initialization + +Initialize once at app startup (e.g., `main.tsx` or `index.ts`), before rendering any authenticated route: + +```typescript +import { Config } from '@forgerock/journey-client'; + +Config.set({ + clientId: '', + redirectUri: window.location.origin + '/callback', + scope: 'openid profile email offline_access', + serverConfig: { + baseUrl: 'https:///am/', + timeout: 5000, + }, + realmPath: 'alpha', // AIC realm; omit for PingAM default realm + journeyName: 'Login', // Entry journey name +}); +``` + +DaVinci variant (replace `@forgerock/journey-client` import): + +```typescript +import { davinci } from '@forgerock/davinci-client'; + +const client = davinci({ + config: { + clientId: '', + redirectUri: window.location.origin + '/callback', + scope: 'openid profile email offline_access', + serverConfig: { baseUrl: 'https://auth.pingone.com//as' }, + }, +}); +``` + +### TokenStorage + +`@forgerock/oidc-client` stores tokens in either `sessionStorage` (default) or `localStorage`. Prefer `sessionStorage` for SPAs to limit the token lifetime to the browser tab. + +Configuration: + +```typescript +import { TokenStorage } from '@forgerock/oidc-client'; + +TokenStorage.set({ store: 'sessionStorage' }); // 'sessionStorage' | 'localStorage' +``` + +Do not persist refresh tokens in `localStorage` for public clients — a compromised token in `localStorage` is exploitable via XSS. Use BFF (Backend For Frontend) pattern with `HttpOnly` cookie token storage for high-security applications. + +### Embedded login vs. hosted login page redirect + +| Mode | Description | When to use | +|---|---|---| +| Embedded login | SDK renders callbacks/collectors inline in the SPA | Full control over UI; acceptable security model for same-origin apps | +| Hosted login redirect | App redirects to Ping-hosted login page; receives tokens via callback | Recommended for cross-origin apps, social login, MFA; simpler CORS requirements | + +For hosted login: call `FRUser.login()` or `client.authorize()` without overriding the login page; the SDK redirects to the configured `redirectUri` with `code` + `state` query params on completion. + +### Handling session callbacks (Journey) + +Journey callbacks arrive as typed objects from the SDK iterator: + +| Callback type | Rendering requirement | +|---|---| +| `NameCallback` | Text input (username field) | +| `PasswordCallback` | Password input | +| `ChoiceCallback` | Radio group or select | +| `TextOutputCallback` | Display-only message (INFO / WARNING / ERROR level) | +| `ConfirmationCallback` | Button group (OK / Cancel) | +| `DeviceProfileCallback` | Silent — SDK collects device fingerprint automatically | +| `HiddenValueCallback` | Silent — SDK handles; no UI required | +| `ValidatedCreateUsernameCallback` | Text input with server-returned validation rules | +| `ValidatedCreatePasswordCallback` | Password input with strength rules | +| `StringAttributeInputCallback` | Text input for a named user attribute (email, phone, etc.) | +| `BooleanAttributeInputCallback` | Checkbox for a boolean attribute | +| `PollingWaitCallback` | Display wait spinner; SDK polls until journey advances | +| `MetadataCallback` | Silent — read `.getValue()` for flow context | +| `SuspendedTextOutputCallback` | Email-suspend message; user told to check email; flow resumes via link | +| `SelectIdPCallback` | IdP selection list; render IdP names/logos | +| `IdPCallback` | Social login button (Google, Apple, Facebook, OIDC) | +| `KbaCreateCallback` | KBA question setup — display question list; collect answer | +| `ReCaptchaCallback` | Google reCAPTCHA widget; submit token on completion | +| `WebAuthnRegistrationCallback` | Trigger FIDO2/passkey registration via browser WebAuthn API | +| `WebAuthnAuthenticationCallback` | Trigger FIDO2/passkey assertion via browser WebAuthn API | + +Pattern: iterate `node.callbacks`, render each by `callback.getType()`, collect user input, call `node.next(updatedCallbacks)`. + +### DaVinci collector types (web) + +For `@forgerock/davinci-client` (PingOne MT), collectors per step: + +| Collector type | Notes | +|---|---| +| `TextCollector` | Text input — username, email | +| `PasswordCollector` | Masked password input | +| `SubmitCollector` | Submit button — call `node.next()` on click | +| `FlowCollector` | Secondary action — "Forgot password", "Register" | +| `SelectCollector` | Dropdown or radio group | +| `SsoCollector` / `IdpCollector` | Social login button (Google, Apple, Facebook) | +| `QrCodeCollector` | QR code display; flow polls for scan completion | +| `PhoneCollector` | Phone number with country picker | + +Auto-advancing (no render): `ProtectCollector` (signals, silent). + +## Generic OIDC integration + +### Authorization code + PKCE for SPAs + +Required parameters for the authorization request: + +| Parameter | Value | +|---|---| +| `response_type` | `code` | +| `client_id` | Registered application client ID | +| `redirect_uri` | Exact match of a registered redirect URI | +| `scope` | `openid` (required) + additional scopes | +| `code_challenge` | Base64URL(SHA256(code_verifier)) | +| `code_challenge_method` | `S256` | +| `state` | Random nonce (CSRF protection) | +| `nonce` | Random nonce (ID token replay protection) | + +Token exchange: POST to the token endpoint with `grant_type=authorization_code`, `code`, `code_verifier`, `redirect_uri`, `client_id`. + +Constraint: `redirect_uri` in the token exchange must be byte-for-byte identical to the one used in the authorization request and the one registered in the admin console. A trailing slash difference or protocol mismatch (`http` vs `https`) causes `invalid_grant`. + +### Client credentials (server-to-server) + +Use `grant_type=client_credentials` for M2M flows where no user is present. The access token represents the client application, not a user. Requires a confidential client (client secret or private key JWT). + +Client credentials tokens do not include `sub` (user subject) in the access token — resource servers must not assume user identity from these tokens. + +### OIDC libraries — framework-agnostic requirements + +No specific library is mandated. Any library implementing RFC 6749 + RFC 7636 works. Requirements for the library: + +- PKCE support (`S256` method) +- State parameter generation and validation +- Nonce validation against the ID token claim +- Token expiry tracking and automatic refresh +- Logout (RP-initiated logout, `end_session_endpoint`) + +Commonly used: `oidc-client-ts`, `@auth0/auth0-spa-js` (when using PingOne as IdP behind a proxy), `AppAuth-JS`. + +### CORS requirements for the token endpoint + +PingOne MT and AIC return `Access-Control-Allow-Origin` headers for cross-origin requests to the token endpoint. PingFederate requires explicit CORS configuration in `pf.properties` or via the PF admin console. + +For SPAs performing the token exchange in-browser (not via a BFF), the token endpoint must allow the app origin. Symptoms of CORS misconfiguration: the authorization request succeeds but the token exchange fails with a network error in the browser console (no response body). + +Constraint: the `OPTIONS` preflight for `/token` must receive `Access-Control-Allow-Origin` and `Access-Control-Allow-Headers: Content-Type, Authorization`. + +## SAML integration + +### OIDC vs. SAML decision rule + +| Condition | Recommendation | +|---|---| +| New SPA or mobile app | OIDC (authorization code + PKCE) | +| Existing enterprise app with SAML SP already configured | Keep SAML — no migration value | +| App needs to federate with multiple IdPs | OIDC (simpler multi-IdP via PingOne as a broker) | +| Legacy .NET / Java EE app using WS-Federation | SAML SP + PingFederate WS-Fed adapter | +| App only needs an API access token | OIDC (client credentials or auth code) | + +SAML does not produce OAuth2 access tokens natively — if the app needs both SSO and API access, consider OIDC or a SAML → OIDC token translation via PingFederate. + +### SP-initiated SSO + +Flow: app generates SAML `AuthnRequest` → POST or redirect to IdP SSO endpoint → IdP authenticates user → IdP POSTs SAML `Response` to ACS URL → app validates assertion → app establishes session. + +Required configuration on the SP side: +- `AssertionConsumerServiceURL` (ACS URL) — must exactly match what is registered at the IdP +- IdP SSO endpoint URL — obtained from IdP metadata +- IdP signing certificate — for validating the assertion signature +- Entity ID — globally unique identifier for the SP + +### IdP-initiated SSO + +Flow: IdP sends unsolicited `Response` to ACS URL — no `AuthnRequest` from SP. Security consideration: IdP-initiated flows are vulnerable to CSRF attacks if the SP does not validate `InResponseTo` (which will be absent). Mitigate with a signed `RelayState` or by restricting IdP-initiated access to known IdPs. + +## Browser-specific auth flows + +### Hosted login page redirect + +Standard flow for SPAs and web apps: + +1. App builds the authorization URL with all required parameters +2. App redirects the browser (full-page or `window.location.href`) +3. User authenticates on the Ping-hosted login page +4. Ping redirects back to `redirect_uri` with `?code=...&state=...` +5. App exchanges `code` for tokens at the token endpoint +6. App validates `state` against the stored value (CSRF check) + +### Popup / post-message flow + +Some apps open the login page in a popup window and receive tokens via `window.postMessage`. This pattern requires: +- The hosted login page to support post-message (PingOne supports this for embedded flows) +- A listener on the parent window: `window.addEventListener('message', handler)` +- Origin validation in the handler: `event.origin === 'https://'` + +Popup flow is blocked by browsers when not triggered from a user gesture (click, keypress). + +### Silent renewal (check session iframe) + +OIDC silent renewal uses a hidden iframe to re-authenticate the user without interaction: + +1. App embeds an iframe pointing to the authorization endpoint with `prompt=none` +2. If the user has an active session, the IdP redirects the iframe to the `redirect_uri` with a new `code` +3. The iframe posts the code to the parent via `postMessage` +4. The parent exchanges the code for a fresh token set + +Constraints: +- Requires the IdP to have a session cookie accessible in the iframe context +- Third-party cookies blocked in Safari (ITP) and Chrome (Privacy Sandbox) break this flow — fallback to full-page redirect or BFF cookie-based session management + +## Flow type comparison table + +| Flow type | Use case | Library type | Token storage | Notes | +|---|---|---|---|---| +| Auth code + PKCE (redirect) | SPA, mobile web | Any OIDC library | `sessionStorage` / `localStorage` | Standard for public clients | +| Auth code + PKCE (popup) | SPA, embedded widget | OIDC library with popup support | In-memory | Blocked without user gesture | +| Client credentials | Server-to-server M2M | HTTP client with OAuth2 support | Server-side only | No user context | +| Hosted login redirect | Any web app | Redirect only; no SDK required | Tokens after callback | Simplest; recommended for cross-origin | +| SAML SP-initiated | Enterprise SSO | SAML library (e.g., passport-saml, Spring SAML) | Server-side session | No access token | +| Silent renewal (iframe) | Token refresh without interaction | OIDC library with iframe support | In-memory | Breaks with third-party cookie restrictions | +| BFF pattern (server-side) | High-security SPA | Server framework + OIDC library | `HttpOnly` cookie | Strongest XSS protection | + +## Prerequisites + +- Application record created in PingOne MT, AIC, or PingFederate with `redirect_uri` registered (use `ping-foundation`) +- Hosted login page or Journey/DaVinci flow operational (use `ping-orchestration`) +- CORS origins configured on the Ping tenant or PingFederate instance for the app's domain +- For SAML: SP entity ID and ACS URL registered at the IdP; IdP metadata downloaded for SP-side validation + +## Common variants + +| Variant | Note | +|---|---| +| PingOne MT | `@forgerock/davinci-client`; discovery endpoint `https://auth.pingone.com//as/.well-known/openid-configuration` | +| AIC / PingOne ST | `@forgerock/journey-client`; realm-specific discovery endpoint | +| PingFederate on-prem | Standard OIDC (no Ping JS SDK required); CORS must be explicitly enabled in PF config | +| React with Vite | Vite dev server proxy can be used to avoid CORS during development; do not proxy token endpoint in production | +| Next.js (SSR) | Use server-side OAuth2 (NextAuth / Auth.js) with PingOne or PingFederate as the provider; do not use client-side PKCE for SSR routes | + +## Related references + +- `references/curated/app-integration-overview.md` — SDK landscape and lifecycle +- `references/curated/mobile-integration-basics.md` — Android and iOS SDK wiring +- `references/curated/integration-troubleshooting-basics.md` — Redirect URI mismatch, CORS errors, token failures + +## Source + +[Ping Identity JavaScript SDK Documentation](https://docs.pingidentity.com/pingone/javascript-sdk/p1_javascript_sdk_landing.html) diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/generated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/cross-platform/top-20.json b/plugins/ping-identity/skills/ping-app-integration/references/generated/cross-platform/top-20.json new file mode 100644 index 0000000..1b95672 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/generated/cross-platform/top-20.json @@ -0,0 +1,74 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-app-integration", + "branch": "cross-platform", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 20, + "docs": [ + { + "title": "Web Integration Basics \u2014 React, JavaScript, OIDC, and SAML", + "slug": "plugins/ping-identity/skills/ping-app-integration/references/curated/web-integration-basics.md", + "url": "https://docs.pingidentity.com/pingone/javascript-sdk/p1_javascript_sdk_landing.html", + "doc_type": "guide", + "capabilities": [ + "app-integration" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Server-Side App Integration \u2014 Backend OIDC and Token Handling", + "slug": "plugins/ping-identity/skills/ping-app-integration/references/curated/server-side-integration-basics.md", + "url": "https://developer.pingidentity.com/pingone-api/platform/", + "doc_type": "guide", + "capabilities": [ + "app-integration" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Mobile Integration Basics \u2014 Android and iOS SDK", + "slug": "plugins/ping-identity/skills/ping-app-integration/references/curated/mobile-integration-basics.md", + "url": "https://docs.pingidentity.com/pingone/native-sdks/p1_native_sdks_landing.html", + "doc_type": "guide", + "capabilities": [ + "app-integration" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Integration Troubleshooting \u2014 Top Failure Modes", + "slug": "plugins/ping-identity/skills/ping-app-integration/references/curated/integration-troubleshooting-basics.md", + "url": "https://docs.pingidentity.com/pingone/troubleshooting/p1_troubleshoot_apps.html", + "doc_type": "troubleshooting", + "capabilities": [ + "app-integration" + ], + "score": 0.97, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Ping Identity App Integration \u2014 Overview", + "slug": "plugins/ping-identity/skills/ping-app-integration/references/curated/app-integration-overview.md", + "url": "https://developer.pingidentity.com/pingone-api/platform/", + "doc_type": "concept", + "capabilities": [ + "app-integration" + ], + "score": 0.9, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/mobile/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/generated/mobile/.gitkeep new file mode 100644 index 0000000..90cd747 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/generated/mobile/.gitkeep @@ -0,0 +1 @@ +_Generated shortlist stub. Populated in Phase 2 by build-reference-manifests CI workflow._ diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/on-prem-integration/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/generated/on-prem-integration/.gitkeep new file mode 100644 index 0000000..90cd747 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/generated/on-prem-integration/.gitkeep @@ -0,0 +1 @@ +_Generated shortlist stub. Populated in Phase 2 by build-reference-manifests CI workflow._ diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/orchestration-sdks/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/generated/orchestration-sdks/.gitkeep new file mode 100644 index 0000000..90cd747 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/generated/orchestration-sdks/.gitkeep @@ -0,0 +1 @@ +_Generated shortlist stub. Populated in Phase 2 by build-reference-manifests CI workflow._ diff --git a/plugins/ping-identity/skills/ping-app-integration/references/generated/web/.gitkeep b/plugins/ping-identity/skills/ping-app-integration/references/generated/web/.gitkeep new file mode 100644 index 0000000..90cd747 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/generated/web/.gitkeep @@ -0,0 +1 @@ +_Generated shortlist stub. Populated in Phase 2 by build-reference-manifests CI workflow._ diff --git a/plugins/ping-identity/skills/ping-app-integration/references/runtime/docs-mcp-routing.md b/plugins/ping-identity/skills/ping-app-integration/references/runtime/docs-mcp-routing.md new file mode 100644 index 0000000..5fe55a3 --- /dev/null +++ b/plugins/ping-identity/skills/ping-app-integration/references/runtime/docs-mcp-routing.md @@ -0,0 +1,42 @@ +--- +title: Docs MCP routing — ping-app-integration +product_family: cross-platform +capabilities: ["app-integration"] +doc_type: reference +status: current +canonical: false +audience: ["developer"] +last_updated: 2026-06-01 +--- + +# Runtime tier — Docs MCP routing for ping-app-integration + +This file describes when and how this skill falls back to live Docs MCP retrieval. It is the third tier in the strategy doc § 0 "Agent Path". + +## When to escalate to Docs MCP + +Use Docs MCP only when: +1. The 1–3 curated anchors loaded from `references/curated/` did not answer the question. +2. The bounded shortlist in `references/generated//top-N.json` did not fill the gap. +3. The user's task requires version-specific, current, or long-tail information. + +If any of these is false, do NOT call Docs MCP. Strategy doc § 0 mandates "use the smallest trusted context first." + +## Surgical query rules + +When Docs MCP is required, query it with: +- The exact platform family (PingOne MT, PingOne ST, Ping Software Suite) +- The exact product or service name +- The exact capability +- A version constraint when applicable + +Retrieve specific sections, not full page dumps. + +## Helix as a runtime path + +Production-bound execution runs through Helix conversation APIs. Runtime selection rule: use the sandbox environment when the request originates from a development or CI context (no real user credentials, feature flags not promoted to production). Use the production Helix tier when the request is user-initiated and requires real token issuance against a production tenant. Helix is **not** a v1 skill; it is a runtime tier referenced from this file. + +## Related + +- `references/curated/` — tier 1 +- `references/generated/` — tier 2 diff --git a/plugins/ping-identity/skills/ping-foundation/SKILL.md b/plugins/ping-identity/skills/ping-foundation/SKILL.md new file mode 100644 index 0000000..70faddb --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/SKILL.md @@ -0,0 +1,120 @@ +--- +name: ping-foundation +description: Platform setup, administration, and core configuration for PingOne MT, PingOne ST (AIC), and on-premises Ping software. Use this skill whenever a user asks ANY question about setting up environments, registering OIDC/SAML apps, managing directories and user populations, configuring authentication policies, branding, or administering PingFederate/PingAccess/PingDirectory/PingID — including advisory, planning, and "how should I..." questions, not just execution tasks. Also invoke with /ping-foundation. +compatibility: Designed for Ping Identity platform tasks. MCP tools for PingOne MT or PingOne ST are used when available; console instructions provided as fallback. +metadata: + publisher: Ping Identity + version: "1.0" +--- + +# ping-foundation + +Platform setup, administration, and core configuration for all Ping Identity deployments. Covers tenant and environment setup, apps, directories, policies, branding, and on-premises software administration. + +> **Role of this skill:** MCP tools handle execution. This skill supplies the context they lack: architecture patterns, correct sequencing, configuration constraints, platform concepts, and guardrails. + +## When to use this skill + +Trigger on ANY question about setting up, configuring, administering, or planning a Ping Identity platform — including advisory and planning requests: + +- Set up or provision environments, tenants, or realms +- Register OIDC, SAML, or OAuth 2.0 applications +- Manage directories, identity stores, or user populations +- Configure authentication policies, sign-on policies, or branding +- Administer PingFederate, PingAccess, PingDirectory, or PingID +- Deploy or upgrade on-premises Ping software +- Advisory: "How should I structure my tenant?", "What client type should I use?" + +## When NOT to use this skill + +- If the primary task is **designing a DaVinci flow or PingOne ST journey**: use `ping-orchestration` +- If the task is **configuring a Universal Service** (Protect, Verify, IGA, Credentials) **as a standalone service**: use `ping-universal-services`. If the task is **wiring a Universal Service into a journey or DaVinci flow** (e.g., adding Verify to a registration journey): use `ping-orchestration` +- If the task is **integrating Ping into an app or SDK**: use `ping-app-integration` +- If unsure which platform: use `ping-quickstart` first + +## Multi-skill use cases + +`ping-foundation` covers the platform layer. Compose with: + +| What comes next | Skill | +|---|---| +| Authentication flow or journey logic | `ping-orchestration` | +| Risk, MFA step-up, Verify, IGA, Credentials | `ping-universal-services` | +| App/SDK integration code | `ping-app-integration` | +| AI agent identity | `ping-identity-for-ai` | + +**Workforce SSO + MFA (PingFederate):** ping-foundation → ping-universal-services (PingID MFA) → ping-app-integration. **CIAM registration (PingOne ST):** ping-foundation → ping-orchestration (registration journey) → ping-universal-services (Verify) → ping-app-integration + +--- + +## Routing — Step 1: Which platform? + +| Platform signal | Branch | +|---|---| +| PingOne admin console, PingOne APIs, PingOne environment | [PingOne MT](#pingone-mt) | +| PingOne ST tenant admin, identity cloud, PingAM, PingIDM, PingDS | [PingOne ST](#pingone-st) | +| PingFederate, PingAccess, PingDirectory, PingID, PingAM standalone | [Ping Software Suite](#ping-software-suite) | + +--- + +## PingOne MT + +**Curated anchors — pick 1–3 matching the task:** + +| Task | Anchor | +|---|---| +| Platform orientation, org/environment structure | `references/curated/cross-platform/foundation-overview.md` | +| Create environment, enable services | `references/curated/pingone-mt/tenant-and-environment-setup.md` | +| Register OIDC, SAML, or Worker app | `references/curated/pingone-mt/app-registration.md` | +| Configure sign-on policy, MFA, step-up | `references/curated/pingone-mt/sign-on-policies.md` | +| Directory, LDAP gateway, populations, groups | `references/curated/pingone-mt/directory-and-populations.md` | +| Admin roles, onboarding administrators | `references/curated/pingone-mt/admin-roles-and-access.md` | +| Themes, branding, custom domain, email/SMS templates, DaVinci UI Studio | `references/curated/pingone-mt/themes-and-branding.md` | +| Cross-platform branding overview | `references/curated/cross-platform/policy-and-branding-basics.md` | + +**Generated shortlist** (fallback): `references/generated/pingone-mt/top-25.json` — sub-files: `tenants.md`, `apps.md`, `policies.md`, `directories.md` + +--- + +## PingOne ST + +**Curated anchors — pick 1–3 matching the task:** + +| Task | Anchor | +|---|---| +| Platform orientation, tenant/realm architecture | `references/curated/pingone-st/foundation-overview.md` | +| Register OIDC, OAuth 2.0, or SAML applications | `references/curated/pingone-st/app-setup.md` | +| Journeys, nodes, realm auth settings | `references/curated/pingone-st/authentication-fundamentals.md` | +| Themes, branding, custom CSS | `references/curated/pingone-st/themes-and-customization.md` | +| Identity store, user schema, LDAP/AD | `references/curated/pingone-st/directory-setup.md` | + +**Generated shortlist** (fallback): `references/generated/pingone-st/top-25.json` + +--- + +## Ping Software Suite + +**Curated anchors — pick 1–3 matching the task:** + +| Task | Anchor | +|---|---| +| PingFederate federation, SP/IdP connections, adapters | `references/curated/ping-software/pingfederate-basics.md` | +| PingDirectory installation, replication, schema | `references/curated/ping-software/pingdirectory-basics.md` | +| PingAccess web app and API protection | `references/curated/ping-software/pingaccess-basics.md` | +| Cross-platform admin patterns (LDAP, OIDC, APIs) | `references/curated/cross-platform/core-admin-patterns.md` | + +**Generated shortlist by product** (fallback): `references/generated/ping-software/top-25.json` + +--- + +## Retrieval and execution + +**Rule:** (1) scan for MCP tools first; (2) load 1–3 curated anchors for the platform/task; (3) fall back to generated shortlist. Full rules: `references/runtime/docs-mcp-routing.md`. + +## Cross-skill escalation + +| If the task also involves... | Reference skill | +|---|---| +| DaVinci flows or PingOne ST journey design | `ping-orchestration` | +| Shared services (Protect, Verify, IGA, Credentials) | `ping-universal-services` | +| App/SDK code integration | `ping-app-integration` | diff --git a/plugins/ping-identity/skills/ping-foundation/ping-marketplace.json b/plugins/ping-identity/skills/ping-foundation/ping-marketplace.json new file mode 100644 index 0000000..9532b26 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/ping-marketplace.json @@ -0,0 +1,37 @@ +{ + "skill_id": "ping-foundation", + "display_name": "Ping Identity Foundation", + "description": "Platform setup, administration, and core configuration for PingOne, PingOne Advanced Identity Cloud, and on-premises Ping software. Covers environments, applications, directories, policies, and branding.", + "version": "1.0.0", + "publisher": "Ping Identity", + "tags": { + "product_family": ["pingone-mt", "pingone-st", "ping-software"], + "products": [ + "pingone", + "pingone-st", + "pingfederate", + "pingaccess", + "pingdirectory", + "pingid", + "pingam" + ], + "capabilities": ["foundation"], + "audience": ["admin", "architect", "operator"], + "use_cases": ["workforce", "customer", "cross-platform"], + "protocols": ["oidc", "saml", "oauth2", "ldap", "scim"] + }, + "entry_point": "SKILL.md", + "references": { + "curated_path": "references/curated/", + "generated_path": "references/generated/" + }, + "related_skills": [ + "ping-quickstart", + "ping-orchestration", + "ping-universal-services", + "ping-app-integration" + ], + "min_context_tokens": 500, + "max_curated_docs": 3, + "max_shortlist_docs": 25 +} diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/core-admin-patterns.md b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/core-admin-patterns.md new file mode 100644 index 0000000..d495d2d --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/core-admin-patterns.md @@ -0,0 +1,157 @@ +--- +title: "Core Admin Patterns" +product_family: cross-platform +products: ["pingone", "pingone-st", "pingfederate", "pingaccess", "pingdirectory", "pingid"] +capabilities: ["foundation"] +audience: ["admin", "operator"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/integrations/p1_ldap_gateways.html" +--- + +# Core Admin Patterns + +Recurring administration patterns across PingOne MT, PingOne ST, and Ping Software Suite. + +## Scope + +Covers: common admin patterns that apply broadly across platforms. +Does NOT cover: deep per-product reference — see generated shortlists for product-specific detail. + +--- + +## Pattern: Connecting an external directory (LDAP/AD) + +**Required fields across all platforms:** + +| Field | Notes | +|---|---| +| LDAP server URL | Use `ldaps://` (TLS); plain LDAP only acceptable in isolated dev environments | +| Bind DN / principal | Service account with read access; write access required if provisioning back | +| Bind password | Store in a secret/ESV, not plaintext config | +| Base DN | Scope the search to the smallest subtree that contains the target users | +| Username attribute | `uid` for LDAP; `sAMAccountName` for AD | +| User search filter | `(uid=*)` for LDAP; `(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))` to exclude disabled AD accounts | + +**Admin surfaces:** +- PingOne MT: Connections → External Directories → + Add Directory +- PingOne ST: Realm → Identity Stores → + Add Identity Store (type: LDAP) via AM admin console +- PingFederate: System → Data Stores → + Add Data Store → LDAP + +--- + +## Pattern: Registering an OIDC application + +**Required fields across all platforms:** + +| Field | Notes | +|---|---| +| Client ID | Unique identifier; auto-generated or custom | +| Client Secret | Required for confidential clients (web apps, M2M); omit for public clients (SPA, native) | +| Redirect URIs | Exact match required; add all environments upfront | +| Grant Types | Authorization Code (web apps), Client Credentials (M2M); avoid Implicit in new apps | +| Scopes | Minimum: `openid`; add `profile`, `email` as needed | + +**Client type decision:** confidential (can hold a secret) vs. public (cannot — SPA or native app). Public clients require PKCE. + +**Admin surfaces:** +- PingOne MT: Applications → + Add Application → OIDC Web App +- PingOne ST: Realm → Applications → OAuth 2.0 → + Create Client (AIC console) or AM console → OAuth 2.0 → Clients +- PingFederate: Applications → OAuth → Clients → + Add Client + +--- + +## Pattern: API-driven configuration + +All platforms expose REST APIs for automation: + +| Platform | Base URL pattern | +|---|---| +| PingOne MT | `https://api.pingone.com/v1/environments/{envId}/...` | +| PingOne ST | `https:///am/json/...` or `https:///openidm/...` | +| PingFederate | `https://:9999/pf-admin-api/v1/...` | +| PingAccess | `https://:9000/pa-admin-api/v3/...` | + +**Authentication per platform:** +- PingOne MT: Worker app (client credentials grant) +- PingOne ST: AM OAuth2 client or IDM service account +- PingFederate / PingAccess: HTTP Basic against the admin API (or OAuth2 if configured) + +--- + +## Pattern: Backup and restore + +| Platform | Mechanism | +|---|---| +| PingOne MT | Admin API environment export; or use frodo-lib / frodo-cli for config export | +| PingOne ST | Admin API exports; or environment-level export from AIC admin console | +| PingFederate | Server → Archive Configuration (ZIP); Git-backed server profiles recommended | +| PingAccess | System → Backup → Export Configuration | +| PingDirectory | `backup` and `restore` CLI tools | + +--- + +## Pattern: Secret and credential rotation + +Rotating client secrets without downtime: + +| Step | Notes | +|---|---| +| 1. Generate new secret in the platform | PingOne MT: app → Client Secret → Rotate; PingFederate: client record → regenerate secret | +| 2. Configure retention window | Keep old secret valid for a rolling period (grace period) while consumers update | +| 3. Update consumers (apps, pipelines) | Deploy new secret to all services using the client | +| 4. Verify old secret is no longer in use | Check access logs for tokens issued with old client secret | +| 5. Remove old secret (or let it expire) | After all consumers are updated | + +For PingOne MT Worker apps: the secret can be retrieved only at creation time; if lost, rotate immediately. + +--- + +## Pattern: Monitoring and health checks + +| Platform | Health check endpoint | +|---|---| +| PingOne MT | No direct health check endpoint; use OIDC discovery document to verify availability: `https://auth.pingone.com//as/.well-known/openid-configuration` | +| PingOne ST | AM health: `https:///am/json/health/live`; IDM health: `https:///openidm/info/ping` | +| PingFederate | `https://:9031/pf/heartbeat.ping` (returns HTTP 200 with body `SERVER_ALIVE`) | +| PingAccess | `https://:3000/pa/heartbeat.ping` (engine node; requires PA version-specific path) | +| PingDirectory | `bin/status` CLI; or LDAP search on `cn=monitor` for operational data | + +--- + +## Pattern: Audit log access + +| Platform | Audit log access | +|---|---| +| PingOne MT | Admin console → Reports → Audit; or Admin API `GET /v1/environments/{envId}/activities` | +| PingOne ST | AM audit log: `https:///am/json/audit/access`; IDM audit: `/openidm/audit/access` | +| PingFederate | Log files in `/log/` — `audit.log`, `server.log`; or syslog forward | +| PingAccess | Log files in `/log/` — `audit.log`; or syslog forward | +| PingDirectory | `/logs/access` — LDAP access log; filterable by operation type | + +## Prerequisites + +Admin access to the relevant platform: PingOne organization admin, PingOne ST superadmin, or server administrator credentials for on-premises products. + +## Common variants + +| Variant | Note | +|---|---| +| API-only configuration | Prefer the REST API for all platforms; use the admin UI only for initial bootstrapping or when an API endpoint is not available | +| Console-driven configuration | Use for one-off tasks, diagnostics, or platform features not yet exposed via API | +| Config-as-code (Ping Software Suite) | Use server profiles (Git-backed) for PingFederate and PingAccess; use frodo-cli for PingOne MT/ST exports | + +## Related references + +- `references/curated/cross-platform/foundation-overview.md` +- `references/curated/cross-platform/tenant-and-environment-setup.md` + +## Source + +[PingOne API Reference](https://apidocs.pingidentity.com/pingone/platform/v1/api/) +[PingFederate Admin API](https://docs.pingidentity.com/pingfederate/latest/admin-api-reference/pf-admin-api-reference.html) +[PingAccess Admin API](https://docs.pingidentity.com/pingaccess/latest/pa-admin-api/pa-admin-api.html) +[PingDirectory Admin Guide](https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-administration-guide/pd-ds-admin-overview.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/foundation-overview.md b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/foundation-overview.md new file mode 100644 index 0000000..99c83b9 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/foundation-overview.md @@ -0,0 +1,153 @@ +--- +title: "Ping Foundation Overview" +product_family: cross-platform +products: ["pingone", "pingone-st", "pingfederate", "pingaccess", "pingdirectory"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: concept +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/introduction_to_pingone/p1_introduction.html" +--- + +# Ping Foundation Overview + +Core concepts for platform setup and administration across all Ping Identity platforms. + +## Scope + +Covers: the shared administrative model across PingOne MT, PingOne ST, and Ping Software Suite. +Does NOT cover: flow design (see `ping-orchestration`) or app integration code (see `ping-app-integration`). + +## Core concepts + +### PingOne MT + +- **Environment**: top-level container; maps to an org or project. Holds apps, populations, policies, and connections. +- **Population**: user store within an environment. Users belong to one population. +- **Application**: OIDC or SAML app registered in PingOne. Policies attach to apps. +- **Sign-on Policy**: ordered set of authentication rules applied to app sign-in. +- **Directory**: user data store; default is PingOne Directory; external LDAP can be connected. + +### PingOne ST + +- **Tenant**: top-level admin unit. One tenant per PingOne ST deployment. +- **Realm**: identity domain inside a tenant. Each realm has its own identity store, policies, and journeys. +- **Identity Store**: PingDS (default) or external LDAP/AD. +- **Journey / Auth Tree**: orchestrated authentication or registration flow. Core orchestration primitive. +- **Theme**: UI customization applied to hosted pages within a realm. + +### Ping Software Suite + +- **Server Profile**: configuration-as-code pattern for PingFederate and PingAccess. +- **Connection**: SP connection (PingFederate) or resource (PingAccess) defines a federation or protection relationship. +- **Adapter**: PingFederate authentication adapter for a specific credential type (HTML form, RADIUS, Kerberos, etc.). +- **Virtual Host**: PingAccess per-site routing configuration. +- **Backend**: PingAccess upstream application definition. + +## Platform capability comparison + +| Capability | PingOne MT | PingOne ST (AIC) | Ping Software Suite | +|---|---|---|---| +| Tenant provisioning | Self-service via admin console | Provided by Ping during onboarding | Self-managed installation | +| Flow authoring | DaVinci flow designer | Journey/tree editor (PingAM) | PingFederate authentication policies + adapters | +| Identity store | PingOne Directory or LDAP Gateway | PingDS (built-in) or external LDAP/AD | PingDirectory or external LDAP/AD | +| Schema extension | Custom attributes (up to 200 per env) | Managed object schema in PingIDM | LDAP schema extension files | +| Customization depth | Moderate — theming, scopes, policies | Deep — custom scripts, nodes, full schema control | Full — server-level config and plugins | +| SLA | Ping-managed per environment type | Ping-managed, production multi-region HA | Customer-managed | +| Data residency | Region selection at env creation | Dedicated instance per customer | Fully customer-controlled | + +## Platform selection decision rules + +| Condition | Recommended platform | +|---|---| +| New greenfield deployment, no data residency requirement | PingOne MT | +| Requires PingAM-native journey nodes or deep scripting customization | PingOne ST (AIC) | +| Existing on-premises infrastructure or strict data residency mandate | Ping Software Suite | +| Hybrid enterprise: cloud SSO + on-prem directory | PingOne MT + LDAP Gateway, or PingFederate + PingDirectory | +| Regulated industry requiring dedicated compute | PingOne ST (AIC) — single-tenant, no shared infrastructure | + +## Setup sequence (generic) + +1. Provision the platform (environment / tenant / server) +2. Configure identity store / directory +3. Register applications +4. Define authentication policy / journey / adapter chain +5. Test sign-in + +## Common gotchas + +| Gotcha | Applies to | Fix | +|---|---|---| +| Redirect URI trailing-slash mismatch | All platforms | Register exact URI; add both forms if the app may send either | +| Environment type is immutable after creation | PingOne MT | Provision the correct type (Sandbox / Development / Production) from the start | +| Realm-scoped resources not visible across realms | PingOne ST | Clients, journeys, and users in `alpha` realm are not accessible in `bravo` | +| Admin API port exposed to internet | Ping Software Suite | Restrict PingFederate port 9999 and PingAccess port 9000 to management CIDR only | +| Service not enabled in environment | PingOne MT | DaVinci, Verify, Protect, etc. must be explicitly activated per environment before use | + +## Prerequisites + +Valid for any administrator with access to the relevant Ping Identity platform (PingOne organization account, PingOne ST subscription, or on-premises server access). + +## Common variants + +| Variant | Note | +|---|---| +| PingOne MT | Multi-tenant cloud; environments managed via console.pingone.com; no infrastructure to operate | +| PingOne ST | Single-tenant SaaS; customer-specific URL; deeper customization, PingAM/IDM/DS stack | +| Ping Software Suite | Self-managed on-prem or IaaS; full infrastructure responsibility; use server profiles for config-as-code | + +## Licensing and feature availability + +| Feature | PingOne MT | PingOne ST (AIC) | Ping Software Suite | +|---|---|---|---| +| Base SSO | Included | Included | PingFederate license | +| MFA | Add-on (PingOne MFA) | Included in AIC | PingID add-on | +| Risk scoring | PingOne Protect add-on | PingProtect node | PingFederate + external risk API | +| Identity proofing | PingOne Verify add-on | PingVerify node (AIC) | Third-party integration | +| SCIM provisioning | Outbound via PingOne | PingIDM connectors | PingDirectory + SCIM plugin | +| Fine-grained authorization | PingOne Authorize add-on | PingAuthorize node | PingAuthorize standalone | + +Licensing is per organization for PingOne MT (enabled per environment). PingOne ST licensing is per tenant subscription. Ping Software Suite products are individually licensed. + +--- + +## Key terminology cross-reference + +| PingOne MT term | PingOne ST equivalent | Ping Software Suite equivalent | +|---|---|---| +| Organization | Tenant | Server installation | +| Environment | Realm | N/A (single server context) | +| Population | User store (managed object type, e.g. `alpha_user`) | LDAP base DN | +| Application | OAuth 2.0 Client | PingFederate SP connection / OAuth client | +| Sign-on policy | Authentication journey / auth tree | Authentication policy (PF 10+) / adapter chain | +| Worker app | Service account (IDM or AM OAuth2 client) | PingFederate OAuth client with client credentials | + +--- + +## Skill routing quick reference + +When a request is about Ping Identity, route through ping-foundation first, then compose with: + +| Next need | Skill | +|---|---| +| Authentication flow or journey logic | `ping-orchestration` | +| Risk scoring, MFA step-up, Verify, IGA, Credentials | `ping-universal-services` | +| App/SDK integration code | `ping-app-integration` | +| AI agent identity | `ping-identity-for-ai` | +| Platform selection / getting started | `ping-quickstart` | + +## Related references + +- `references/curated/cross-platform/tenant-and-environment-setup.md` +- `references/curated/cross-platform/policy-and-branding-basics.md` +- `references/curated/cross-platform/core-admin-patterns.md` + +## Source + +[Ping Identity Documentation](https://docs.pingidentity.com/) +[PingOne MT Documentation](https://docs.pingidentity.com/pingone) +[PingOne ST (AIC) Documentation](https://docs.pingidentity.com/pingoneaic) +[PingFederate Documentation](https://docs.pingidentity.com/pingfederate) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md new file mode 100644 index 0000000..ad3f43e --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md @@ -0,0 +1,152 @@ +--- +title: "Policy and Branding Basics" +product_family: cross-platform +products: ["pingone", "pingone-st"] +capabilities: ["foundation"] +audience: ["admin"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/authentication/p1_authenticationpolicies.html" +--- + +# Policy and Branding Basics + +Core configuration decisions for authentication policy and UI branding across PingOne MT and PingOne ST. + +## Scope + +Covers: sign-on policies, MFA policies, themes, and hosted page customization. +Does NOT cover: DaVinci flow design or PingOne ST journey logic — see `ping-orchestration`. + +--- + +## PingOne MT — Authentication Policies + +**Sign-on policy structure:** +- An ordered list of actions evaluated at sign-in +- Actions: Login, MFA, Identity Verification, Progressive Profiling, Agreement +- Policies attach to one or more applications; each app can reference a different policy + +**Action conditions:** always, risk-based (requires PingOne Protect), or device-based + +**MFA policy:** +- Controls which MFA methods are allowed: TOTP, SMS, email OTP, FIDO2, passkeys +- Configured at environment level; can be overridden per application +- Must be assigned to a sign-on policy's MFA action to take effect + +**Admin surface:** Policies → Sign-on → + Add Policy + +--- + +## PingOne MT — Branding + +| Setting | Location | +|---|---| +| Logo, colors, favicon | Branding → Edit | +| Email / SMS notification templates | Branding → Email / SMS templates | +| Custom domain (required for branded hosted pages) | Settings → Custom Domains | + +--- + +## PingOne ST — Policies + +- Authentication policies are realm-scoped +- Auth trees/journeys define the login flow — see `ping-orchestration` for journey design +- Policy sets apply authorization rules after authentication completes + +**Journey-as-policy:** In PingOne ST, the journey itself is the authentication policy. Bind a journey to the realm default or to a specific application to control which flow is used. + +**Admin surface:** Realm → Authentication → Trees or Journeys + +--- + +## PingOne ST — Theming + +For PingOne ST theming configuration, see `references/curated/pingone-st/themes-and-customization.md`. + +--- + +## Notification templates + +Both PingOne MT and PingOne ST support custom email and SMS notification templates. + +### PingOne MT + +| Template type | Admin surface | +|---|---| +| Email templates | Branding → Email Templates | +| SMS templates | Branding → SMS Templates | +| Notification sender | Settings → Notifications → Senders | + +Email templates support HTML with merge variables (e.g., `${user.username}`, `${user.name.given}`). Verify sender domain ownership before go-live; unverified domains use Ping's default sending address. + +### PingOne ST + +Notification templates are managed at the realm level. PingOne ST supports locale-specific templates — add locale variants under the template editor to serve translated content based on the user's browser language. + +--- + +## Branding checklist (pre-go-live) + +| Item | PingOne MT | PingOne ST | +|---|---|---| +| Custom domain configured and DNS verified | Settings → Custom Domains | Tenant Settings → Custom Domains | +| Logo uploaded | Branding → Logo | Theme Editor → Logo | +| Brand colors set | Branding → Colors | Theme Editor → Colors | +| Email/SMS sender verified | Settings → Notifications | Notifications → Email provider | +| Notification templates customized | Branding → Email / SMS templates | Realm → Notifications → Templates | +| Error page branding applied | Branding (limited control) | Custom CSS in Theme Editor | + +--- + +## Common gotchas + +| Gotcha | Applies to | Fix | +|---|---|---| +| Notification sent from Ping default address | PingOne MT | Verify sender domain under Settings → Notifications → Senders before testing emails | +| MFA policy not taking effect despite being configured | PingOne MT | Policy must be referenced inside an MFA action in a sign-on policy, and that policy must be attached to the application | +| Journey is not branded | PingOne ST | Assign the theme to the realm (default) or to the specific journey in journey settings | +| Custom font not loading | PingOne ST | Add font origin to Content Security Policy (CSP) settings; CSP blocks unconfigured external origins | +| Policy change affects all apps silently | PingOne MT | Apps with no explicit policy assignment use the environment default — change to default affects all such apps simultaneously | + +## Prerequisites + +Admin access to the target platform (PingOne organization admin for MT; AIC tenant admin for ST). + +## Common variants + +| Variant | Note | +|---|---| +| Workforce vs. CIAM | Workforce MFA policy typically uses TOTP/push; CIAM policies often add risk-based conditions and progressive enrollment | +| Multi-application policies | PingOne MT supports per-application policy override; PingOne ST uses per-journey auth assignment | +| Layered branding | PingOne MT supports global branding overridden per environment; PingOne ST supports realm and journey-level theme override | + +--- + +## PingFederate — Authentication policies (on-premises) + +PingFederate 10+ replaces per-SP adapter configuration with centralized authentication policies — directed graphs of authentication sources and contract mappings. + +| Concept | Description | +|---|---| +| Authentication policy | Directed graph; nodes are adapters or IdP connections; branches are outcomes | +| Contract | Set of attributes passed through the policy; mapped to SAML assertions or OIDC claims at the SP end | +| Selector | Evaluates context (IP, device, claim) to route to different authentication paths | +| Composite adapter | Chains two adapters (e.g., HTML Form + PingID) to implement MFA within a single policy node | + +--- + +## Related references + +- `references/curated/cross-platform/foundation-overview.md` +- `references/curated/cross-platform/tenant-and-environment-setup.md` +- `references/curated/cross-platform/core-admin-patterns.md` + +## Source + +[PingOne sign-on policies](https://docs.pingidentity.com/pingone/latest/platformconsole/p1_c_sign_on_policies.html) +[PingOne branding](https://docs.pingidentity.com/pingone/latest/platformconsole/p1_c_branding.html) +[PingOne ST authentication journeys](https://docs.pingidentity.com/pingoneaic/am-journey-guide/journey-overview.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/tenant-and-environment-setup.md b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/tenant-and-environment-setup.md new file mode 100644 index 0000000..5f0a807 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/tenant-and-environment-setup.md @@ -0,0 +1,155 @@ +--- +title: "Tenant and Environment Setup" +product_family: cross-platform +products: ["pingone", "pingone-st"] +capabilities: ["foundation"] +audience: ["admin"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/environments/p1_c_environments.html" +--- + +# Tenant and Environment Setup + +Provisioning requirements and key configuration decisions for a new PingOne environment or PingOne ST tenant. + +## Scope + +Covers: initial provisioning and configuration of environments/tenants. +Does NOT cover: on-prem server installation (see `references/curated/cross-platform/core-admin-patterns.md`). + +## PingOne MT — New Environment + +**Admin surface:** console.pingone.com → Environments → + Add Environment + +**Required decisions:** +- Environment type: Sandbox, Development, or Production — determines SLA and capability profile +- Region selection: affects data residency +- Services to enable: MFA, Verify, DaVinci, Risk, etc. must be explicitly activated per environment +- Initial population: create an admin population or connect an external directory before adding users + +**Key settings to establish before adding apps or users:** + +| Setting | Location | +|---|---| +| Custom domain | Settings → Custom Domains | +| Notification sender (email/SMS) | Settings → Notifications | +| Default sign-on policy | Policies → Sign-on | + +--- + +## PingOne ST — New Tenant + +**Admin surface:** Your PingOne ST tenant URL (provided by Ping during onboarding) + +**Required decisions:** +- Identity store: PingDS (default, no setup needed) or external LDAP/AD (requires additional configuration) +- Realm usage: `alpha` and `bravo` realms exist by default; decide which to use for customer-facing vs. internal flows before registering apps +- Custom domain: must be configured before go-live for production tenants + +**Key settings to establish before adding journeys or apps:** + +| Setting | Location | +|---|---| +| Identity store selection | Realm → Identity Stores | +| Custom domain | Tenant Settings → Custom Domains | +| Email provider | Email → SMTP or Ping-managed | +| Default theme | Themes → create or edit per realm | + +**Common post-setup tasks:** +- Import users or configure LDAP sync +- Enable social providers (Google, Apple, etc.) +- Configure federation (SAML SP or OIDC RP) + +## Pre-go-live checklist (both platforms) + +Complete these before routing live users to the environment: + +| Item | PingOne MT | PingOne ST | +|---|---|---| +| Custom domain configured | Settings → Custom Domains | Tenant Settings → Custom Domains | +| Email sender verified | Settings → Notifications → Senders | Notifications → Email provider | +| MFA / authentication policy in place | Policies → Sign-on | Realm → Authentication → Journeys | +| Application registered with correct redirect URIs | Applications → + Add Application | Applications → OAuth 2.0 Clients | +| Branding applied (logo, colors) | Branding | Realm → Theming | +| Population / identity store configured | Directory → Populations | Realm → Identity Stores | +| Notification templates customized | Branding → Email / SMS templates | Realm → Notifications → Templates | + +--- + +## Configuration-as-code and promotion patterns + +| Pattern | PingOne MT | PingOne ST | Ping Software Suite | +|---|---|---|---| +| Export configuration | Admin API export; or `frodo-cli` | Admin API export; or frodo-cli | Git-backed server profile; or archive ZIP | +| Import to another environment | Admin API import; or frodo-cli push | Admin API import; or frodo-cli push | Redeploy from server profile Git branch | +| CI/CD integration | Worker app (client credentials) to authenticate pipeline API calls | AM/IDM service accounts from pipeline | Server profile Git push triggers config rebuild | +| Secrets management | Store client secret in pipeline secret store; never in source code | ESV (Environment-Specific Variables) for runtime secrets | Vault integration or environment variable injection at container startup | +| Secrets management | Store client secret in pipeline secret store; never in source code | ESV (Environment-Specific Variables) in PingOne ST for secrets and config values | + +--- + +## Common gotchas + +| Gotcha | Applies to | Fix | +|---|---|---| +| Environment type immutable after creation | PingOne MT | Verify type (Sandbox / Development / Production) before creating; cannot be changed | +| Region immutable after environment creation | PingOne MT | Select data residency region at creation time; cannot be changed after | +| Services must be explicitly activated per environment | PingOne MT | DaVinci, Verify, Protect, etc. must be toggled on per environment — they are not on by default | +| Custom domain required for production hosted login | Both | Users see Ping's default domain without a custom domain configuration | +| Realms in PingOne ST cannot be merged | PingOne ST | Plan realm architecture (alpha vs. bravo vs. additional) before onboarding users; migrating users between realms is manual | + +## Prerequisites + +- **PingOne MT:** PingOne organization account with admin access. Admin role: Environment Admin or Organization Admin. DNS control for custom domain configuration. +- **PingOne ST:** PingOne ST subscription; tenant URL and initial superadmin credentials from onboarding email. Custom domain DNS control for production tenants. +- **Ping Software Suite:** Java 11 or 17 JRE; license file from Ping Identity; Linux or Windows server meeting minimum hardware requirements per product. + +## Common variants + +| Variant | Note | +|---|---| +| Multi-region HA tenants | PingOne ST production tenants support multi-region high availability; configured during Ping Identity onboarding | +| Sandbox-to-production promotion | Export configuration from dev/staging using frodo-cli or admin API; import to production tenant | +| Environment-per-pipeline | PingOne MT supports multiple environments (dev, staging, prod) within one organization; use environment types to control SLA | +| Blue/green deployment (Ping Software) | Run two server profile branches; switch load balancer to the new profile after validation; rollback by switching back | +| Secrets rotation without downtime | Store secrets in a vault (HashiCorp Vault, AWS Secrets Manager); configure products to read secrets at startup; rotate in vault first, then restart | + +## Ping Software Suite — Server provisioning overview + +On-premises Ping software requires manual provisioning steps beyond cloud platform onboarding. License files must be obtained from Ping Identity before installation; the server will not start without a valid license. + +| Product | Provisioning steps | Documentation anchor | +|---|---|---| +| PingFederate | Install, license, configure admin console, LDAP data store | `references/curated/ping-software/pingfederate-basics.md` | +| PingDirectory | Install, setup wizard (non-interactive via `--cli`), replication | `references/curated/ping-software/pingdirectory-basics.md` | +| PingAccess | Install, license, configure token provider (PingFederate or PingOne) | `references/curated/ping-software/pingaccess-basics.md` | + +All three products support Git-backed server profiles for configuration-as-code deployments. Server profiles are the recommended approach for container-based (Docker/Kubernetes) deployments. + +--- + +## Related references + +- `references/curated/cross-platform/foundation-overview.md` — platform family orientation and capability comparison +- `references/curated/cross-platform/policy-and-branding-basics.md` — authentication policy and branding once tenant is provisioned +- `references/curated/pingone-mt/tenant-and-environment-setup.md` — PingOne MT-specific environment configuration details +- `references/curated/pingone-st/foundation-overview.md` — PingOne ST tenant architecture and admin surfaces + +## Common gotchas across platforms + +| Gotcha | Applies to | Fix | +|---|---|---| +| License file missing before first start | Ping Software Suite | Obtain a valid license file from Ping Identity support before installation; the server will not start | +| Admin pop-up blocks service activation prompt | PingOne MT | Service activation requires an explicit step in the environment settings; it is not triggered automatically when creating apps | +| Config promotion missing schema extensions | PingOne ST | Export schema changes from IDM first; import them before importing managed object data | + +## Source + +[PingOne environments](https://docs.pingidentity.com/pingone/environments/p1_c_environments.html) +[PingOne ST tenant administration](https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-create_tenant.html) +[PingOne MT environment types](https://docs.pingidentity.com/pingone/environments/p1_environment_types.html) +[frodo-cli for PingOne config export](https://github.com/rockcarver/frodo-cli) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingaccess-basics.md b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingaccess-basics.md new file mode 100644 index 0000000..ff70aff --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingaccess-basics.md @@ -0,0 +1,196 @@ +--- +title: "PingAccess — Administration Basics" +product_family: ping-software +products: ["pingaccess"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingaccess/9.0/pa_landing_page.html" +--- + +# PingAccess — Administration Basics + +Reverse-proxy policy enforcement point that protects web applications and APIs by validating tokens issued by a connected authorization server before forwarding requests to backend sites. + +## Scope + +**Covers:** PingAccess deployment models, core entities (Virtual Host, Site, Application, Web Session, Resource, Rule), token provider setup, end-to-end web app and API protection configuration, common gotchas. +**Does NOT cover:** PingFederate federation configuration (see `references/curated/ping-software/pingfederate-basics.md`), PingDirectory (separate anchor), OAuth/OIDC client design on PingOne MT/ST (ping-foundation MT/ST anchors), PingID MFA configuration. + +--- + +## What PingAccess is + +PingAccess is a reverse proxy and policy enforcement point. It sits in front of web applications and APIs, intercepts inbound requests, and enforces authentication and authorization based on tokens issued by a connected authorization server — PingFederate, PingOne, or PingOne Advanced Identity Cloud. PingAccess does not issue tokens; it validates them via introspection or JWT signature verification, then either forwards the request to the backend site or rejects/redirects the caller. + +--- + +## Core architecture + +| Component | Role | +|---|---| +| Token Provider (PingFederate or PingOne) | Issues tokens; PingAccess validates them via introspection or JWKS-based JWT verification | +| Virtual Host | Hostname:port that PingAccess listens on for inbound client requests | +| Site | Backend target URL (upstream application or API server) | +| Application | Maps a virtual host + context root to a site; access policies and rules attach here | +| Resource | Path-level rule entry within an application; controls which URL patterns require which level of authentication | +| Web Session | OIDC-based cookie session for browser flows; manages token acquisition and lifecycle | +| Access Token Validator (ATV) | Validates OAuth bearer tokens for API protection flows | +| Rule / Rule Set | Access control logic (OAuth scope checks, header assertions, network range, time range, Groovy scripts) | +| Identity Mapping | Passes identity attributes downstream to the backend as HTTP headers or JWTs | +| Agent | Lightweight module (Apache, NGINX, IIS, Java SDK) that offloads policy decisions to a PingAccess policy server without a full proxy hop | + +--- + +## Deployment models + +| Model | Description | Pros | Cons | +|---|---|---|---| +| Gateway | PingAccess proxies all traffic; enforces policy before forwarding to backend | Full feature set; centralized audit logging; simplest troubleshooting | Requires network restructuring; adds a request hop | +| Agent | Lightweight module on the web server coordinates with PA policy server | No network changes; cached decisions can reduce latency | Per-server maintenance; agents must be upgraded independently; URL/response rewriting unavailable | +| Sideband | API gateway (Kong, Apigee) makes a backchannel call to PA for policy decisions | No network changes; centralized logging | Integration kits must be maintained per gateway; feature availability depends on gateway | + +Gateway is the recommended starting point for new deployments. Use Agent or Sideband when network topology prevents a proxy insertion. + +--- + +## Setup sequence — protecting a web app (gateway) + +Steps must be completed in this order; later steps depend on objects created earlier. + +| Step | Object | Key fields | +|---|---|---| +| 1 | **Token Provider** | Connection to PingFederate or PingOne; issuer URL; trusted certificate group; introspection endpoint or JWKS URI | +| 2 | **Site** | Name; Targets (`hostname:port`); Secure (HTTPS requires Trusted Certificate Group); Load Balancing Strategy | +| 3 | **Virtual Host** | Host (e.g., `app.corp.com`); Port (e.g., `443`); TLS certificate association | +| 4 | **Web Session** | Name; Cookie Type; Audience; Client ID; Client Credentials; Idle Timeout; Max Timeout | +| 5 | **Rule** (optional) | Name; Type (e.g., OAuth Scope, HTTP Request Header); assertion logic | +| 6 | **Identity Mapping** (optional) | Name; Type (Header Identity Mapping); Attribute → Header mapping | +| 7 | **Application** | Name; Context Root (starts `/`, no trailing `/`); Virtual Host; Application Type = Web; Web Session; Destination = Site | +| 8 | **Resource** | Path Patterns (start `/`); Resource Authentication = Standard or Anonymous; Methods | + +**Verification:** Access the virtual host URL without a session; PingAccess should redirect the browser to the authorization server login page. + +--- + +## Web session configuration + +| Field | Recommended value | Notes | +|---|---|---| +| Cookie Type | Encrypted JWT | Authenticated encryption (confidentiality + integrity + authenticity); default | +| Audience | Match RP client ID or agreed string (1–32 chars) | Tokens not matching this value are rejected and trigger reauthentication | +| OpenID Connect Login Type | Code | Standard authorization code flow; most compatible | +| Idle Timeout | 30–60 min | Resets on activity; default is 60 min | +| Max Timeout | 8–12 h for workforce | Absolute limit regardless of activity; default is 240 min | +| Cookie Domain | `.parent-domain.com` | Set to parent domain for multi-subdomain apps; omitting causes per-host token regeneration | +| Secure Cookie | `true` in all production environments | Must be HTTPS or authentication fails | +| HTTP-Only Cookie | `true` | Blocks JavaScript access to session cookie | +| SameSite | Lax (default) | Use `None` only for cross-site API consumers requiring cookies | +| Enable PKCE | `true` (default) | SHA-256 code challenge; do not disable | +| Cache User Attributes | Enable if cookie size approaches 4096 bytes | Stores attributes server-side; reduces cookie payload | + +--- + +## API protection vs. web app protection + +| Scenario | Use Web Session? | Token validation method | Auth challenge response | +|---|---|---|---| +| Browser app (user-facing) | Yes | OIDC authorization code flow; Web Session manages cookie lifecycle | Redirect to AS login page | +| API — machine-to-machine | No | JWT bearer validation (local JWKS) or token introspection | 401 / 403 | +| API — SPA with bearer token | No | JWT bearer validation or introspection via ATV | 401 / 403 | +| Mixed app (browser routes + API routes) | Conditional | Web Session for `/app/*` paths; Bearer/ATV for `/api/*` paths | Per route type | + +For API protection: create Application Type = **API**, configure an Access Token Validator under Access > Token Validation, and attach it to the application. Introspection requires a configured introspection endpoint in the token provider. + +--- + +## Token provider configuration + +Two validation modes; choose based on token type and latency requirements. + +| Mode | Mechanism | Token types supported | Latency | Configuration | +|---|---|---|---|---| +| Introspection | PingAccess calls AS `/introspect` per request | Opaque tokens and JWTs | Higher (network call per request) | Token Provider > introspection endpoint URL + client credentials | +| JWT verification | PingAccess validates JWT locally using JWKS | JWTs only | Lower (no per-request network call) | Token Provider > JWKS URI; AS must publish JWKS endpoint | + +**PingFederate token provider — required configuration objects on the PF side (in order):** + +1. Password credential validator +2. IdP adapter +3. Default scope definition +4. Access token manager +5. IdP adapter mapping and access token mapping +6. OpenID Connect policy +7. Resource server client (grant type: Access Token Validation) +8. Web session client (used for browser OIDC flows in PA) + +After PF configuration: export PF runtime certificate → import into PA → add to Trusted Certificate Group → configure Settings > Token Provider > PingFederate Runtime. + +**Constraint:** Saving a new PingFederate runtime configuration overwrites any existing one. There is no merge. + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Token introspection endpoint not configured | 401 on every request despite valid session | Verify introspection endpoint URL and client credentials under Token Provider; check connectivity from PA to AS | +| Web session cookie domain too narrow | Login loop when navigating between subdomains | Set Cookie Domain to `.parent-domain.com`; invalid domain value causes endless reauthentication | +| Site URL missing context root | 404 on all requests forwarded to backend | Verify Site Targets includes any required base path; context root in the Application is a listener path, not forwarded to backend unless the site URL accounts for it | +| CORS errors on API routes | API clients receive CORS rejection | PingAccess does not add CORS headers automatically; add a Cross-Origin Request Rule explicitly; do not use wildcard `*` in Allowed Origins (insecure) | +| Expired web session not refreshing | Users see repeated login prompts | Check Max Timeout vs. refresh token expiry; enable "Validate Session" if PF session state synchronisation is needed | +| Cookie size exceeds 4096 bytes | Browser silently drops cookie; session lost | Disable "Send Token" on Site; enable "Cache User Attributes" in Web Session; simplify crypto algorithms (ECDSA P-256, AES-128-CBC+HMAC-SHA256) | +| Secure Cookie enabled in non-HTTPS environment | Authentication always fails | Disable Secure Cookie in dev/non-TLS environments; never disable in production | +| Context Root `/pa` conflicts | Application fails to save or path unreachable | `/pa` is reserved for PingAccess internal resources; choose a different context root or enable "Use context root as reserved resource base path" in advanced settings | +| API app type introspection not available | Error: "Cannot use remote validation as authorization server does not have an Introspection endpoint" | Configure introspection endpoint in PF OAuth settings; ensure the resource server client has the correct grant type | +| PF runtime config overwritten | Existing PF token provider configuration disappears | Saving a new PingFederate runtime configuration is destructive; back up existing config before making changes | + +--- + +## Prerequisites + +- PingAccess 9.x license installed +- Java 17 JRE (verify version matrix for target release) +- A configured token provider: PingFederate (minimum 10.x for authentication policies), PingOne, or PingOne AIC +- TLS certificate for the Virtual Host listener; trusted certificate group for backend Site if Site uses HTTPS +- OAuth client credentials (Client ID + Secret or Mutual TLS key pair) registered on the token provider for the Web Session + +--- + +## Common variants + +| Variant | Note | +|---|---| +| High availability (HA) | Multiple PA engine nodes behind a load balancer; shared operational database (PostgreSQL); console node separate from engine nodes | +| Agent-based deployment | Deploy PA agents on Apache, NGINX, or IIS when full proxy insertion is not possible; agents offload policy decisions to a central PA policy server | +| Sideband with Kong or Apigee | PA acts as a policy decision point; API gateway integration kit makes backchannel calls; no proxy hop required | +| Container / Kubernetes | Environment variable overrides for all configuration settings; recommended with PingAccess server profile (Git-backed config) | +| FIPS mode | Supported; restricts available crypto algorithms; verify token provider crypto compatibility before enabling | + +--- + +## Related references + +- `references/curated/ping-software/pingfederate-basics.md` — PingFederate adapter chains, SP/IdP connections, OAuth client configuration +- `references/curated/cross-platform/foundation-overview.md` +- `references/curated/cross-platform/core-admin-patterns.md` + +--- + +## Source + +[PingAccess 9.0 Landing Page](https://docs.pingidentity.com/pingaccess/9.0/pa_landing_page.html) +[How PingAccess works](https://docs.pingidentity.com/pingaccess/9.0/introduction_to_pingaccess/pa_how_does_pa_work.html) +[Choosing a deployment model](https://docs.pingidentity.com/pingaccess/9.0/introduction_to_pingaccess/pa_choose_a_deployment_model.html) +[Creating web sessions](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_user_interface_reference_guide/pa_creating_web_sessions.html) +[Advanced web session settings](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_user_interface_reference_guide/pa_advanced_web_session_settings.html) +[Application field descriptions](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_user_interface_reference_guide/pa_application_field_descriptions.html) +[Site field descriptions](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_user_interface_reference_guide/pa_site_field_descriptions_ref.html) +[Protecting a web app (gateway)](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_use_cases/pa_protecting_a_web_app_with_pa_in_a_gateway_deployment.html) +[Protecting an API (gateway)](https://docs.pingidentity.com/pingaccess/9.0/pingaccess_use_cases/pa_protecting_an_api_with_pa_in_a_gateway_deployment.html) +[Minimizing cookie size](https://docs.pingidentity.com/pingaccess/9.0/troubleshooting/pa_minimizing_the_pa_cookie_size.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md new file mode 100644 index 0000000..28f21cd --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md @@ -0,0 +1,160 @@ +--- +title: "PingDirectory — Administration Basics" +product_family: ping-software +products: ["pingdirectory"] +capabilities: ["foundation"] +audience: ["admin", "operator"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-administration-guide/pd-ds-admin-overview.html" +--- + +# PingDirectory — Administration Basics + +Initial installation, configuration, and operation patterns for PingDirectory on Linux. + +## Scope + +**Covers:** Linux installation, initial setup wizard, replication, schema extension, backup/restore, and connection profiles for PingFederate integration. +**Does NOT cover:** PingDirectoryProxy or PingDataSync (separate products). PingFederate SP connection setup — see `references/curated/ping-software/pingfederate-basics.md`. + +--- + +## Installation on Linux + +**Supported platforms:** Red Hat Enterprise Linux, CentOS, Ubuntu, Debian (see release notes for exact version support). + +**Prerequisites:** +- Java 11 or 17 JRE (set `JAVA_HOME`) +- Minimum 4 GB RAM for production; 2 GB for development +- Sufficient file descriptor limits (`ulimit -n` should be ≥65535) + +**Install steps:** + +| Step | Command / Notes | +|---|---| +| Extract archive | `unzip PingDirectory-.zip -d /opt/` | +| Run setup wizard | `/opt/PingDirectory/setup` | +| Accept license | Interactive prompt; or use `--acceptLicense` for non-interactive | +| Specify base DN | e.g., `dc=example,dc=com` — base DN is created during setup | +| Set LDAP/LDAPS ports | Default: LDAP 389, LDAPS 636, admin LDAP 4444 | +| Create initial admin user | Specify Directory Manager DN and password | +| Enable replication (optional) | Configure after primary server is running | + +**Non-interactive setup example:** +``` +/opt/PingDirectory/setup --cli \ + --acceptLicense \ + --baseDN dc=example,dc=com \ + --ldapPort 389 \ + --ldapsPort 636 \ + --adminConnectorPort 4444 \ + --rootUserPassword \ + --hostname localhost +``` + +--- + +## Server management tools + +| Tool | Path | Purpose | +|---|---|---| +| `start-ds` | `bin/start-ds` | Start the server | +| `stop-ds` | `bin/stop-ds` | Stop the server | +| `status` | `bin/status` | Check server status and connection handlers | +| `ldapmodify` | `bin/ldapmodify` | Apply LDIF changes to directory data | +| `ldapsearch` | `bin/ldapsearch` | Query directory data | +| `dsconfig` | `bin/dsconfig` | Modify server configuration (connection handlers, backends, etc.) | +| `manage-topology` | `bin/manage-topology` | Manage replication topology | + +--- + +## Schema extension + +Add custom attributes or object classes via LDIF schema files: + +1. Create a custom schema file: `/opt/PingDirectory/config/schema/99-custom.ldif` +2. Define `attributeTypes` and `objectClasses` in LDIF format +3. Restart the server or use `dsconfig` to reload schema online + +**Important:** PingFederate and PingIDM expect specific attribute names. Align custom attribute names with the consuming application's configuration before extending schema. + +--- + +## Replication + +PingDirectory uses multi-master replication. All servers in a topology are equal peers. + +**Enable replication:** +``` +bin/dsreplication enable \ + --host1 --port1 4444 --bindDN1 "cn=Directory Manager" --bindPassword1 \ + --host2 --port2 4444 --bindDN2 "cn=Directory Manager" --bindPassword2 \ + --replicationPort1 8989 --replicationPort2 8989 \ + --baseDN dc=example,dc=com --adminUID admin --adminPassword +``` + +**Initialize replication data:** +``` +bin/dsreplication initialize-all --baseDN dc=example,dc=com --adminUID admin --adminPassword +``` + +--- + +## Backup and restore + +| Operation | Command | +|---|---| +| Backup | `bin/backup --backupAll --backupDirectory /opt/backups/$(date +%Y%m%d)` | +| Restore | `bin/restore --backupDirectory /opt/backups/20260519 --task` | +| Encrypt backups | Add `--encrypt` to backup command (requires server encryption settings) | + +Schedule backups via `dsconfig` recurring tasks or an external scheduler. + +--- + +## Connection profile for PingFederate + +When PingFederate connects to PingDirectory as its data store: + +| PingFederate data store field | Value | +|---|---| +| LDAP type | PingDirectory | +| Host | `` | +| Port | 636 (LDAPS) | +| Bind DN | Service account DN with read-only access | +| Base DN | `dc=example,dc=com` | +| Username attribute | `uid` (or `mail` for email login) | +| User search filter | `(uid=*)` | + +--- + +## Prerequisites + +- Linux server with Java 11 or 17 JRE installed +- PingDirectory license file +- `JAVA_HOME` set to the Java installation directory +- File descriptor limits raised (`ulimit -n 65535` or persistent via `/etc/security/limits.conf`) + +## Common variants + +| Variant | Note | +|---|---| +| Replicated pair | Two PingDirectory servers in active-active replication for HA; typical minimum for production | +| PingDirectory + PingFederate | Standard workforce SSO topology; PingDirectory is the LDAP data store for PingFederate's HTML Form Adapter | +| External users (SCIM inbound) | PingIDM provisions users to PingDirectory via SCIM 2.0 or LDAP connector | +| Container deployment | Official Docker image available; mount configuration volume; `--acceptLicense` required for non-interactive startup | + +## Related references + +- `references/curated/cross-platform/core-admin-patterns.md` +- `references/curated/ping-software/pingfederate-basics.md` + +## Source + +[PingDirectory Administration Guide](https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-administration-guide/pd-ds-admin-overview.html) +[PingDirectory setup guide](https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-admin-guide/pd-ds-installing.html) +[PingDirectory replication](https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-administration-guide/pd-ds-replication-overview.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md new file mode 100644 index 0000000..ed76f28 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md @@ -0,0 +1,156 @@ +--- +title: "PingFederate — Administration Basics" +product_family: ping-software +products: ["pingfederate"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingfederate/latest/administrators_reference_guide/pf-admin-guide.html" +--- + +# PingFederate — Administration Basics + +Core configuration concepts for PingFederate: server architecture, SP/IdP connections, adapter chains, and initial setup. + +## Scope + +**Covers:** PingFederate deployment architecture, SP connections, IdP connections, authentication adapter chains, OAuth/OIDC client setup, initial admin setup. +**Does NOT cover:** PingAccess (resource gateway), PingDirectory (directory services), or PingID MFA configuration — each has its own generated reference under `references/generated/ping-software/`. + +--- + +## Server architecture + +PingFederate is a standalone federation server deployed as a Java application. + +| Component | Role | +|---|---| +| Engine node | Handles runtime federation traffic (SSO, token, OAuth) | +| Console node | Hosts the admin UI and API | +| Cluster | Multiple engine nodes behind a load balancer for HA | + +**Admin API base URL:** `https://:9999/pf-admin-api/v1/` +**Admin console URL:** `https://:9999/pingfederate/app` + +--- + +## SP connection (PingFederate as IdP) + +An SP connection defines a SAML 2.0 or WS-Federation trust between PingFederate (acting as IdP) and a service provider (e.g., Salesforce, Workday). + +**Required fields:** + +| Field | Notes | +|---|---| +| Partner entity ID | Unique identifier for the SP; from SP metadata XML | +| ACS URL | Assertion Consumer Service — where PingFederate POSTs the SAML response | +| Name ID format | `email`, `persistent`, or `transient`; dictated by SP requirements | +| Attribute contract | User attributes to include in the SAML assertion (e.g., `mail`, `givenName`) | +| Authentication source | Which adapter or IdP adapter chain handles authentication for this SP | +| Signing | Sign assertions, responses, or both; export signing cert to SP admin | + +**Fastest path:** Import SP metadata XML to auto-populate entity ID, ACS URL, and certificates. + +--- + +## IdP connection (PingFederate as SP) + +An IdP connection defines a SAML 2.0 trust with an upstream identity provider (e.g., Azure AD, Okta, another PingFederate). + +**Required fields:** + +| Field | Notes | +|---|---| +| Partner entity ID | Unique identifier for the upstream IdP | +| SSO service URL | IdP's SAML SSO endpoint | +| Signature verification cert | IdP's signing certificate; import from IdP metadata | +| Attribute mapping | Map incoming SAML attributes to PingFederate's attribute contract | + +--- + +## Authentication adapter chain + +PingFederate uses adapters to collect and validate credentials. Adapters chain together to implement multi-factor and step-up flows. + +**Common adapters:** + +| Adapter | Purpose | +|---|---| +| HTML Form Adapter | Username/password collection and validation against a data store | +| Kerberos Adapter | Transparent Windows SSO via Kerberos/SPNEGO | +| RADIUS Adapter | Integration with RADIUS-based MFA (RSA, Cisco ISE) | +| Composite Adapter | Chain multiple adapters; first-factor + second-factor composition | +| PingOne MFA Adapter | Step-up MFA via PingOne MFA service | + +**Authentication policy:** Authentication policies (PingFederate 10+) replace adapter-per-SP configuration with a centralized policy graph. Define the policy once; reference it from multiple SP connections. + +--- + +## OAuth 2.0 / OIDC client setup + +**Admin surface:** Applications → OAuth → Clients → + Add Client + +**Required fields:** + +| Field | Notes | +|---|---| +| Client ID | Unique identifier; auto-generated or custom | +| Client Secret | Required for confidential clients; omit for public clients | +| Redirect URIs | Exact match required | +| Grant types | Authorization Code, Client Credentials, Refresh Token | +| Scopes | Define allowed scopes; map to attribute contracts for claims | + +**OIDC discovery:** `https://:9031/.well-known/openid-configuration` + +--- + +## PingDirectory integration + +PingFederate typically uses PingDirectory (or Active Directory) as the identity store. + +**Data store configuration:** System → Data Stores → + Add Data Store → LDAP + +Required fields: LDAP URL (`ldaps://`), Bind DN, Bind password, Base DN, Username attribute. + +--- + +## Prerequisites + +- PingFederate license file installed +- Java 11 or 17 JRE (version requirements vary by PingFederate release) +- Server profile (Git-backed) recommended for config-as-code deployments +- Network access to identity stores (LDAP/AD) and any integrated MFA services + +## Common variants + +| Variant | Note | +|---|---| +| Clustered deployment | Engine nodes behind a load balancer; console node separate; requires shared operational data store (PostgreSQL or Oracle) | +| PingFederate + PingDirectory | Most common workforce SSO pattern; PingDirectory serves as the identity store with LDAP | +| Server profile (config-as-code) | Recommended for production; all config stored in Git and applied at container startup | +| PingFederate as OAuth AS | Used when PingFederate issues tokens to downstream APIs; requires Access Token Manager configuration | + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| SP connection using wrong ACS URL | SAML response POSTed to wrong endpoint; SP returns error | Import SP metadata to auto-populate ACS URL; verify against SP documentation | +| Admin console port exposed | PF admin API accessible from internet | Restrict port 9999 to management CIDR; use HTTPS only | +| LDAP pool exhaustion | Authentication latency spikes under load | Increase LDAP data store connection pool size in System → Data Stores | +| Clock skew exceeds SAML tolerance | SAML assertion rejected as expired or not yet valid | Verify NTP sync on PingFederate server and all SP/IdP hosts; default tolerance is 5 minutes | + +## Related references + +- `references/curated/cross-platform/foundation-overview.md` +- `references/curated/cross-platform/core-admin-patterns.md` + +## Source + +[PingFederate Administration Guide](https://docs.pingidentity.com/pingfederate/latest/administrators_reference_guide/pf-admin-guide.html) +[PingFederate Admin API reference](https://docs.pingidentity.com/pingfederate/latest/admin-api-reference/pf-admin-api-reference.html) +[SP connection configuration](https://docs.pingidentity.com/pingfederate/latest/administrators_reference_guide/pf-creating-sp-connection.html) +[Authentication policies](https://docs.pingidentity.com/pingfederate/latest/administrators_reference_guide/pf-authn-policies.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md new file mode 100644 index 0000000..30ccea2 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md @@ -0,0 +1,185 @@ +--- +title: "PingOne MT — Administrator Roles and Access Management" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/administrators/p1_admin_roles.html" +--- + +# PingOne MT — Administrator Roles and Access Management + +Configuring administrator roles, scoping access to environments and populations, and onboarding new administrators in PingOne MT. + +## Scope + +**Covers:** Built-in and custom administrator roles, role scoping (org / environment / population), three methods for adding administrators (individual, group, invitation), admin email verification, and the Administrators environment best practice. + +**Does NOT cover:** End-user identity management — see `references/curated/pingone-mt/directory-and-populations.md`; application roles (PingOne Authorize authorization) — distinct from admin roles; PingOne ST admin configuration. + +--- + +## Built-in administrator roles + +PingOne uses a flat role model — there is no super-admin role with global permissions. Every role grants a specific set of permissions scoped to a level. + +| Role | Key permissions | +|---|---| +| Organization Admin | Create/edit/delete environments; manage organization settings | +| Environment Admin | Manage environments, populations, password policies; invite roles | +| Identity Data Admin | Create/edit users; reset passwords; manage user attributes | +| Client Application Developer | Create/edit/delete applications; reset client secrets | +| Identity Data Read Only | Read-only subset of Identity Data Admin | +| Configuration Read Only | Read-only subset of Environment Admin | + +Additional SSO roles delegate access to connected products (PingOne AIC, PingFederate) — scoped to the connected product's admin surface only. + +**No role hierarchy:** You cannot assign a role that you do not hold. An Organization Admin cannot grant more permissions than their own grant level. + +**Privileged permissions:** Some permissions grant access to sensitive data or destructive operations (delete environment, view personal user data). Assign these sparingly and review before delegating. + +--- + +## Custom roles + +Custom roles delegate a restricted subset of permissions for a specific resource set. + +| Property | Detail | +|---|---| +| Scope | Environment-specific — must be recreated in each environment where needed | +| Who can create | Organization Admin, Custom Roles Admin, or a custom role with equivalent Custom Roles Admin permissions | +| Assignment levels | Organization, environment, or population | +| Permission set | Subset of built-in permissions; cannot exceed the creating admin's own permissions | + +Custom roles are the right tool when a business unit needs admin access to its own population without visibility into others. + +--- + +## Role scoping levels + +A role assignment always combines: **role** + **assignee** (user or group) + **scope** (where permissions apply). + +| Level | Covers | Typical use | +|---|---|---| +| Organization | All environments in the org | Org Admin overseeing the entire tenant | +| Environment | One or more named environments | Environment Admin scoped to Dev + Staging only | +| Population | One population within an environment | Identity Data Admin scoped to the "Customers-EMEA" population only | + +Scoping to a population means the admin cannot see or manage users in other populations in the same environment. + +--- + +## Administrators environment + +PingOne automatically creates an **Administrators environment** when the organization is provisioned. + +| Rule | Detail | +|---|---| +| Best practice | Create all administrator identities in the Administrators environment; do not co-locate admins with end users | +| Why | Prevents privilege escalation; simplifies access audit | +| Cross-environment admin access | A user in the Administrators environment can be granted Environment Admin or Identity Data Admin for any other environment — the identity stays in the Administrators environment | +| Getting Started Guide toggle | Settings > Environment Properties > Getting Started Guides — show or hide the admin wizard on the Environments page | + +Older organizations may have co-located admin and end-user identities. If restructuring, plan a user migration before splitting environments. + +--- + +## Adding administrators — three methods + +### Method 1: Assign roles to an individual user + +1. In the target environment, go to Directory > Users. +2. Select or create the user; click the Roles tab > Grant Roles. +3. Select the role and scope (environment or population); click Save. +4. Send the user the Console Login URL (Settings > Environment Properties) and their PingOne username. +5. On the user's Profile tab, click Verify to send the email verification. + +**Email verification is required** before an administrator can access the console. + +### Method 2: Grant roles to a group + +Groups allow role assignment to scale across many users at once. + +1. Go to Directory > Groups; create or select a group. +2. Add the target users to the group (Add/Remove Users). +3. On the group's Roles tab, click Grant Roles; select role + scope. +4. All current and future members of the group inherit the role automatically. + +**Prerequisite:** Performing admin must have Identity Data Admin role (or equivalent) to create or edit groups. + +### Method 3: Invite an administrator to register + +Use when the target user does not yet have a PingOne account. + +1. Go to Directory > Users > Invite Admin. +2. Enter name and email; select a population. +3. Set invitation expiry (max 24 hours); assign roles on the next step. +4. Click Send Invitation. Invitation appears in the Users list with an active/revoke toggle. +5. Invitee clicks Complete Registration in email, pastes the invite code, sets a password, and verifies email. + +**Prerequisites:** PingOne must be the identity provider; administrator security must be enabled with PingOne or a hybrid authentication source. + +--- + +## Console Login URL + +Each environment has a unique **Console Login URL** distinct from the main `console.pingone.com` entry point. Provide this URL to new administrators alongside their username so they access the correct environment directly. + +**Location:** Settings > Environment Properties > URLs + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Admin cannot access console after role assignment | Login succeeds but no environments or settings visible | Verify email on the user's Profile tab; confirm role scope includes the target environment | +| Invitation code rejected | "Invalid code" error during registration | Invitation may have expired (max 24h); resend the invitation; old code is invalidated | +| Cannot assign a role you don't hold | Role assignment blocked or role not visible in dropdown | Contact an Org Admin to escalate; you cannot delegate above your own grant level | +| Group role not inherited by new member | New group member lacks expected admin access | Check the group's Roles tab; re-save the assignment if it shows correctly but isn't propagating | +| Admin and end users co-located | Hard to audit; privilege escalation risk | Move admin identities to the Administrators environment | +| Custom role missing in another environment | Expected custom role not available | Custom roles are environment-scoped; recreate in each environment | +| Admin invited but PingOne not configured as IdP | Invite flow blocked | Confirm PingOne is the identity provider and administrator security is enabled in environment settings | + +--- + +## Prerequisites + +- PingOne organization with Organization Admin or Environment Admin role +- For invitations: PingOne configured as the identity provider; administrator security enabled +- For group-based role assignment: Identity Data Admin role in the target environment + +--- + +## Common variants + +| Variant | Pattern | +|---|---| +| Org-wide operations team | Assign Organization Admin; scope covers entire org | +| Per-environment team lead | Environment Admin scoped to Dev + Staging; no Production access | +| Per-population HR data steward | Identity Data Admin scoped to the HR population; cannot see other populations | +| Automated admin via Worker app | Create a Worker application; assign it Organization Admin or Environment Admin; use client credentials tokens for API-driven configuration | + +--- + +## Related references + +- `references/curated/pingone-mt/tenant-and-environment-setup.md` — environment creation and initial configuration +- `references/curated/pingone-mt/directory-and-populations.md` — population, group, and user management +- `references/curated/pingone-mt/app-registration.md` — application management and developer role + +--- + +## Source + +- https://docs.pingidentity.com/pingone/administrators/p1_admin_roles.html +- https://docs.pingidentity.com/pingone/administrators/p1_managing_administrators.html +- https://docs.pingidentity.com/pingone/administrators/p1_adding_administrators.html +- https://docs.pingidentity.com/pingone/administrators/p1_invite_admin.html +- https://docs.pingidentity.com/pingone/getting_started_with_pingone/p1_getting_started.html diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/app-registration.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/app-registration.md new file mode 100644 index 0000000..bf52d5d --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/app-registration.md @@ -0,0 +1,226 @@ +--- +title: "PingOne MT — Application Registration" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +services: ["oidc", "saml"] +audience: ["admin", "developer"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/applications/p1_applications_add_applications.html" +--- + +# PingOne MT — Application Registration + +Registering OIDC, SAML, and Worker applications in PingOne MT: required fields, grant type selection, public-vs-confidential client rules, and common failure modes. + +## Scope + +**Covers:** OIDC Web, Native, Single-Page App, Worker, and SAML application creation and post-creation configuration in PingOne MT (console.pingone.com). +**Does NOT cover:** PingOne ST application setup (separate platform); Journey/DaVinci flow design — see `skills/ping-orchestration/SKILL.md`; SDK wiring and token consumption — see `skills/ping-app-integration/SKILL.md`. + +--- + +## Application types + +| Type | Client model | Typical grant types | Notes | +|---|---|---|---| +| OIDC Web App | Confidential (server-side) | Authorization Code | Holds a client secret; server renders HTML | +| Native / Mobile App | Public | Authorization Code + PKCE | Mobile (iOS, Android); no secret stored on device | +| Single-Page App (SPA) | Public | Authorization Code + PKCE | Front-end only; cannot hold a client secret | +| Worker | Confidential | Client Credentials | M2M / admin automation; no user sign-on by default | +| Device Authorization | Public | Device Authorization + PKCE | Constrained devices (smart TV, CLI); user approves on second device | +| SAML Application | N/A (SAML) | SP-initiated or IdP-initiated | Browser SSO only; no token endpoint | + +**Decision rule — public vs. confidential:** +- If the app runs in a browser or on a user's device, treat it as **public**: set Token Endpoint Auth Method to `none` and enforce PKCE. +- If the app runs on a server you control and the secret never leaves that server, treat it as **confidential**: use `client_secret_basic` or `client_secret_post`. + +--- + +## Required fields at creation time + +The only fields collected when first creating an app are Name, Type, and (for SAML) ACS URL + Entity ID. All OIDC-specific fields are configured post-creation. + +| Field | Applies to | Requirement | Notes | +|---|---|---|---| +| Application Name | All | Required | Unique within the environment; max 256 characters | +| Application Type | All | Required | Cannot be changed after creation | +| ACS URLs | SAML | Required | At least one; first entry is the default | +| Entity ID | SAML | Required | Unique within the environment | +| SP Metadata | SAML | Optional | Import XML or metadata URL to auto-fill ACS URL and Entity ID | + +**After creation, the app is disabled by default.** Enable it explicitly before testing. + +--- + +## OIDC configuration fields (post-creation) + +### Identifiers + +| Field | Notes | +|---|---| +| Client ID | Auto-generated; note immediately — used in all token requests | +| Client Secret | Confidential clients only; cannot be retrieved after the creation screen; rotate immediately if lost | + +### Grant types and response types + +| Grant type | Use case | PKCE required | +|---|---|---| +| Authorization Code | User-facing web and mobile apps | Required for public clients; recommended for confidential | +| Client Credentials | M2M / Worker | No (no user context) | +| Refresh Token | Offline access | No | +| Device Authorization | Constrained devices | Optional | +| Implicit | Legacy only | No — do not use for new apps | + +Enable only the grant types the application will actually use. Unused grant types increase attack surface. + +### Token endpoint authentication method + +| Method | Client type | Notes | +|---|---|---| +| None | Public (SPA, Native) | No secret; PKCE enforced instead | +| Client Secret Basic | Confidential | Secret sent in HTTP Authorization header (Base64-encoded) | +| Client Secret Post | Confidential | Secret sent in request body | +| Client Secret JWT | Confidential | Secret used to sign a JWT assertion | +| Private Key JWT | Confidential | Asymmetric key pair; strongest confidential option | + +### Redirect URIs + +- Exact-match enforced: the URI in the authorization request must match a registered URI character-for-character. +- Fragment components (`#`) are not allowed in registered URIs. +- Trailing slashes matter: `https://app.example.com/callback` and `https://app.example.com/callback/` are different entries. +- In production, wildcard patterns are disabled by default. Register every environment's URI explicitly. + +### Scopes + +| Scope | Included by default | Notes | +|---|---|---| +| `openid` | Required minimum | Must be present; triggers ID token issuance | +| `profile` | Optional | Claims: name, given_name, family_name, etc. | +| `email` | Optional | Claim: email | +| `address` | Optional | Claim: address | +| `phone` | Optional | Claim: phone_number | +| Custom scope | Optional | Must be defined as a Resource first, then assigned to the app | + +### Refresh token settings + +| Field | Default | Range | Notes | +|---|---|---|---| +| Duration | 30 days | 60s – 1826 days | Absolute expiry from issuance | +| Rolling Duration | 180 days | 60s – 1826 days | Must be >= Duration | +| Rolling Grace Period | 0 | 0 – 86,400 seconds | Window during which the previous refresh token remains valid after rotation | +| Token format | Opaque | Opaque or JWT | JWT refresh token format is deprecated; retires 2027-03-01 | + +--- + +## SAML-specific fields (post-creation) + +| Field | Notes | +|---|---| +| ACS URLs | If the SP sends `AssertionConsumerServiceURL` in the auth request, it must exactly match a registered ACS URL | +| Entity ID | Used as the SAML Issuer; must match what the SP sends | +| Subject NameID Format | Default: `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified` | +| Signing Key | Certificate PingOne uses to sign assertions and SLO messages | +| Sign options | Sign Assertion (default), Sign Response, or Sign Assertion & Response | +| Signing Algorithm | RSA-SHA256 / RSA-SHA384 / RSA-SHA512 or EC equivalents | +| Encryption | Optional; AES-128 or AES-256 (AES-256 recommended); SAML 2.0 only | +| SLO Endpoint | URL on the SP that receives SLO requests from PingOne | +| SLO Binding | HTTP POST (default) or HTTP Redirect | +| SLO Window | Min 1 hour, max 24 hours | + +--- + +## Sign-on policy attachment + +An application with no explicit sign-on policy uses the **environment default policy**. In production, always attach a named policy to each application. + +- Sign-on policies are configured under Authentication in the left navigation. +- Attach a policy to an app via the app's Sign-on Policies tab. +- Multiple policies can be assigned; PingOne evaluates them in priority order. + +**Risk:** Relying on the environment default means a policy change affects all unattached apps simultaneously — silent behavior change. + +--- + +## Population assignment + +Users must belong to a population that is permitted to access the application. + +- Population assignment is configured on the app's Allowed Populations tab. +- If a user's population is not in the allowed list, authentication will be denied. +- A mismatch produces a generic access-denied response that does not name the population as the cause — hard to diagnose without checking the app's allowed population list. + +## Group-based access + +In addition to population-level access, applications support group-based access control. + +- Configure on the app's Access tab. +- Select one or more groups; only members of those groups can access the application. +- Combine with dynamic groups (see directory-and-populations.md) for attribute-driven access — for example, only users with `countryCode = US` in the target group can access the app. +- Group access and population access are both enforced — a user must satisfy both conditions. + +## Application SSO URLs + +After enabling the application, the Overview tab provides: +- **Initiate Single Sign-On URL** — the entry point to start the SSO flow; share with users or embed in application launchers. +- **OIDC applications:** Discovery document at `https://auth.pingone.com/{envId}/as/.well-known/openid-configuration`. +- **SAML applications:** IdP-initiated SSO URL and SP metadata download link are on the Configuration tab. + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| App left disabled after creation | Token requests fail immediately; no error details | Enable the app toggle after creation | +| Redirect URI mismatch | `redirect_uri_mismatch` or `invalid_request` | Register the exact URI including scheme, port, path, and trailing slash | +| Missing PKCE on public client | `invalid_grant` from modern OIDC libraries | Set Token Endpoint Auth Method to None and enable PKCE | +| Client secret lost | Cannot retrieve after creation screen | Rotate the secret via the console or API; update all consumers | +| Population not allowed | Silent access-denied; user cannot authenticate | Add the user's population to the app's Allowed Populations list | +| Custom scope not granted | Scope absent from access token | Define the scope as a Resource, then assign the Resource to the app | +| Implicit grant on new app | Insecure; tokens in URL fragment | Remove Implicit grant; use Authorization Code + PKCE | +| JWT refresh token in use | Will break 2027-03-01 | Switch to Opaque token format before the retirement date | + +--- + +## Prerequisites + +- PingOne MT environment with Environment Admin or Application Admin role +- For SAML: SP metadata (ACS URL and Entity ID) from the service provider +- For custom scopes: Resource definition created before app registration +- For sign-on policies: Policy defined in the environment before attachment + +--- + +## Common variants + +| Variant | Note | +|---|---| +| Multi-environment redirect URIs | Register all environment-specific redirect URIs (dev, staging, prod) in a single app, or use one app registration per environment | +| Custom domain impact on redirect URIs | If using a custom domain, redirect URIs must match the custom domain hostname — not `auth.pingone.com` | +| Worker app for API automation | Create a Worker app; assign admin roles to it; use client credentials grant to obtain tokens for API calls | +| PKCE for confidential clients | PKCE is valid on Authorization Code for confidential clients too; adds defense-in-depth against authorization code interception | +| Secret rotation with zero downtime | Configure a retention window for the previous secret so existing tokens remain valid during the rotation window | + +--- + +## Related references + +- `references/curated/pingone-mt/tenant-and-environment-setup.md` — environment prerequisites before app registration +- `skills/ping-orchestration/SKILL.md` — Journey and DaVinci flow wiring +- `skills/ping-app-integration/SKILL.md` — SDK-level token consumption and OIDC client setup + +--- + +## Source + +[Adding applications in PingOne](https://docs.pingidentity.com/pingone/applications/p1_applications_add_applications.html) +[PingOne application types](https://docs.pingidentity.com/pingone/applications/p1_application_types.html) +[Editing OIDC applications](https://docs.pingidentity.com/pingone/applications/p1_edit_application_oidc.html) +[Editing SAML applications](https://docs.pingidentity.com/pingone/applications/p1_edit_application_saml.html) +[Editing native applications](https://docs.pingidentity.com/pingone/applications/p1_edit_application_native.html) +[Editing worker applications](https://docs.pingidentity.com/pingone/applications/p1_edit_application_worker.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/directory-and-populations.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/directory-and-populations.md new file mode 100644 index 0000000..ab8872a --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/directory-and-populations.md @@ -0,0 +1,298 @@ +--- +title: "PingOne MT Directory Options and Population Management" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/directory/p1_aboutusers.html" +--- + +# PingOne MT Directory Options and Population Management + +Configure the user directory model and population structure for a PingOne MT environment before creating applications or sign-on policies. + +## Scope + +**Covers:** PingOne Directory (built-in cloud store), LDAP Gateway (proxy to on-premises LDAP/AD), external identity providers (OIDC, SAML, social), and population management in PingOne MT environments. + +**Does NOT cover:** +- PingOne ST directory configuration — see `references/curated/pingone-st/directory-setup.md` +- Provisioning workflow design (inbound/outbound SCIM, HR sync) — use the `ping-orchestration` skill +- SDK-level user management (create/update users via API in an app) — use the `ping-app-integration` skill +- Sign-on policy design — see `references/curated/pingone-mt/sign-on-policies.md` +- Administrator role assignment — see `references/curated/pingone-mt/admin-roles-and-access.md` + +--- + +## Directory options + +| Option | Description | Typical use case | Sync model | +|---|---|---|---| +| **PingOne Directory** | Built-in cloud directory; user records stored natively in PingOne per environment | New deployments; cloud-first; no existing enterprise directory | None — PingOne is the system of record | +| **LDAP Gateway** | Agent deployed on-premises proxies authentication and optional user sync to an existing LDAP or AD directory | Enterprise with existing AD; no data migration required immediately | Read-through (auth delegates; no copy) or Import (copy on first auth or scheduled sync) | +| **External IdP (OIDC/SAML/Social)** | PingOne acts as SP/RP; external IdP authenticates; JIT provisioning creates a local record on first login | Federation with corporate SSO, partner IdP, or consumer identity provider | JIT — record created in PingOne on first successful authentication | + +Multiple directory options can coexist in one environment (e.g., PingOne Directory for employees and Google social IdP for customers). + +--- + +## Populations + +### What populations are + +A population is the primary organizational unit for users within a PingOne environment. Every user belongs to **exactly one** population at any time. + +Populations determine: +- Which password policy applies to the user +- Which applications the user can access (via the app's **Allowed Populations** setting) +- Which sign-on policy conditions evaluate (policies can branch on population membership) +- Which administrator can manage the user (an Identity Admin role can be scoped to a single population) +- Which default identity provider applies when a user has no authoritative IdP set + +### Population assignment rules + +- A user cannot belong to more than one population simultaneously. +- At least one population must exist before any user can be created. +- New users with no explicit population assignment are placed in the **default population**. +- Any population can be designated the default; only one default exists at a time. +- Users can be moved between populations after creation. + +### Population strategy patterns + +| Pattern | Configuration | +|---|---| +| Single audience (e.g., workforce only) | One population; rename the auto-created default to reflect its purpose | +| Multiple audiences (employees + customers) | Separate population per audience; assign each app to the correct population | +| Multi-region or multi-brand | One population per region or brand; scope admin roles to each population | +| Delegated admin | Create a population per business unit; assign Identity Admin role scoped to that population | + +**Silent failure warning:** A user who exists in the directory but is not in the population allowed by the application will be denied access with no error surfaced to the user. Verify the app's Allowed Populations list when debugging unexpected access denials. + +--- + +## PingOne Directory configuration + +PingOne Directory requires no external components. A default population is created automatically with each new environment. + +### Password policy + +Password policies are assigned at the population level. Each population can have a distinct policy. + +| Field | Constraint | +|---|---| +| Minimum length | 8–255 characters | +| Complexity rules | Uppercase, lowercase, number, special character toggles | +| Expiry | Days until password expires; 0 = never expires | +| Lockout threshold | Number of failed attempts before account lock | +| Lockout duration | Minutes; 0 = manual admin unlock required | + +### Custom user attributes + +Custom attributes extend the PingOne user schema beyond built-in fields (name, email, username, address, etc.). + +| Field | Constraint | +|---|---| +| Name | Unique identifier; used in token mappings | +| Display Name | UI label | +| Type | String (with optional uniqueness, multivalued, enumerated, or regex validation) or JSON (with optional JSON Schema validation) | +| Max custom attributes | 200 string or JSON attributes per environment | +| Multivalued | Once set, cannot be changed to single-valued | +| Unique | Value must be unique across the entire environment | + +**Admin surface:** Environment → Directory → User Attributes + +Custom attributes are mapped to OIDC claims, SAML assertions, or access tokens via application-level attribute mapping configuration. + +--- + +## LDAP gateway + +The LDAP gateway is a software agent deployed on-premises, adjacent to the LDAP or AD directory. It communicates outbound to PingOne over WebSocket Secure (WSS) — no inbound firewall rule is required. + +### Supported directory types + +PingDirectory, Microsoft Active Directory, Oracle Directory Server Enterprise Edition, Oracle Unified Directory, CA Directory, IBM Security Directory Server, and any LDAP v3-compliant server. + +### Gateway connection fields + +| Field | Requirement | Notes | +|---|---|---| +| Name | Required | Unique within the environment | +| LDAP Directory Type | Required | Determines available features (e.g., Kerberos for AD) | +| LDAP Host Name | Required | Multiple hosts supported for failover; tried in order | +| Port | Required | Default 389; use 636 for LDAPS (required in production) | +| Connection Security | Required | TLS (recommended), StartTLS, or None | +| Bind DN | Required | Service account with user search permissions | +| Bind Password | Required | Read-only sufficient for auth-only; write access needed for provisioning back to LDAP | +| Follow LDAP Referrals | Optional | Relevant for multi-domain AD forests | + +**AD-only additional fields:** Kerberos authentication toggle, Service Account UPN (case-sensitive), Service Account Password, Retain Previous Credentials (up to five), Retention Duration (default 610 minutes). + +### User type fields (per gateway) + +| Field | Notes | +|---|---| +| Password Authority | PingOne or LDAP — determines which system validates credentials | +| User Search Base DN | Scope to the smallest required subtree | +| User Link Attributes | Attributes used to match LDAP user to PingOne record (e.g., `dn`, `sAMAccountName`; evaluated as OR chain) | +| Enable migration on first auth | Copies user into PingOne Directory on first successful LDAP authentication | +| LDAP Filter | Narrows which users are eligible for migration | +| Target Population | PingOne population where imported users are placed | +| Update PingOne attributes on sign-on | Syncs mapped attributes on each authentication; if any attribute update fails, the authentication also fails | +| Attribute Mappings | Maps LDAP attributes to PingOne schema fields (e.g., `mail` → `email`, `sAMAccountName` → `username`) | + +### Gateway agent prerequisites + +| Requirement | Specification | +|---|---| +| CPU | 2 vCPUs | +| RAM | 1 GB | +| Storage | 1 GB | +| Runtime | Docker, standalone Java 21 LTS, or Windows application | +| Outbound network | WSS to PingOne regional gateway endpoint (e.g., `wss://gateways.pingone.com/`) | +| Directory account | Bind DN + password with user search permissions | +| Admin role | Environment Admin in PingOne | + +--- + +## External identity providers + +External IdPs federate authentication into PingOne. Users are auto-provisioned (JIT) into a designated population on first login. + +### Supported types + +| Type | Configuration source | +|---|---| +| OIDC | Discovery Document URI or manual endpoint entry | +| SAML 2.0 | Metadata URL or manual entity ID + SSO URL + certificate | +| Social: Google, Apple, Facebook, GitHub, LinkedIn, Microsoft, Amazon, PayPal, X, Yahoo | App credentials from the provider's developer console | + +### OIDC IdP required fields + +| Field | Constraint | +|---|---| +| Client ID | Required | +| Client Secret | Required | +| Discovery Document URI | Recommended — auto-populates issuer, JWKS, authorization, and token endpoints | +| Issuer | Must use `https` | +| Authorization Endpoint | Must use `https` | +| Token Endpoint | Must use `https` | +| Token Endpoint Authentication Method | None, Client Secret Basic, or Client Secret Post | +| Requested Scopes | Space-separated, case-sensitive | +| Target Population | Population where JIT-provisioned users are created | + +### SAML IdP required fields + +| Field | Constraint | +|---|---| +| IdP Entity ID | Required | +| SSO Endpoint | Required | +| SSO Binding | HTTP POST or HTTP Redirect | +| Verification Certificate | Required | +| Signing Algorithm | RSA_SHA256/384/512 or EC variants | +| Target Population | Population where JIT-provisioned users are created | + +### Attribute mapping + +Both OIDC and SAML IdPs support attribute mapping (provider attribute → PingOne schema field). The **Update Condition** controls whether PingOne overwrites the local value: `Empty Only` (only when the PingOne field has no value) or `Always` (overwrite on every login). + +--- + +## Groups + +Groups are collections of users that share something in common (department, region, role). A user can belong to **multiple groups** but only one population at a time. Groups are secondary to populations; populations are the primary organizational unit. + +### Group types + +| Type | Icon in console | How members are managed | +|---|---|---| +| Internal | Internal Group | Created and managed entirely in PingOne; add/remove members directly | +| External | External Group | Created through a connected external IdP or LDAP gateway; membership flows from the external source | + +### Group membership modes + +| Mode | How members are added | When to use | +|---|---|---| +| Static | Manually added or removed | Fixed, known set of users (e.g., a pilot group) | +| Dynamic | Filter expression on user attributes (e.g., `countryCode = US`) | Auto-include users matching criteria; membership updates automatically as attributes change | +| Combined | Both static additions and a dynamic filter | Base membership via filter; manually add exceptions | + +**Dynamic filter behavior:** When the filter expression changes, group membership updates immediately. When a user's attributes change to match or no longer match the filter, they are added or removed automatically — no manual step required. + +### Group-level role assignment + +Groups can be granted administrator roles, so all group members inherit the role. See `references/curated/pingone-mt/admin-roles-and-access.md` for the full procedure. + +### Group creation fields + +| Field | Notes | +|---|---| +| Group Name | Must be unique within the environment (for environment groups) or within the population (for population groups) | +| Population | Optional — assign to scope group to a population; required when Identity Admin role is used instead of Environment Admin | +| Dynamic filter | Attribute + operator + value (e.g., `country Equals US`); preview shows matching count before saving | + +**Group creation prerequisites:** Identity Data Admin role (or equivalent) required to create or edit groups. + +--- + +## Common failure modes + +| Symptom | Likely cause | Resolution | +|---|---|---| +| LDAP gateway shows as disconnected | Gateway agent not running, or outbound WSS blocked by firewall | Verify agent process is running; confirm port 443/WSS to regional gateway endpoint is open | +| User authenticated but denied by app | User's population is not in the app's Allowed Populations list | Add the user's population to the app's Allowed Populations setting | +| External IdP attribute not appearing in token | Provider attribute not mapped to a PingOne schema field | Add the mapping in the IdP's attribute mapping configuration | +| New user creation fails with schema error | Custom attribute limit reached or unique constraint violated | Check environment's attribute count; verify uniqueness requirement against existing data | +| Password policy not enforced for new users | Policy not assigned to the user's population | Assign the password policy to the correct population | +| JIT provisioning fails | Target population not set on the IdP configuration | Set the Population field on the IdP to the intended registration population | +| `Update PingOne attributes on sign-on` causes auth failure | An LDAP attribute update fails (field conflict, type mismatch) | Audit attribute mappings; fix the conflicting field or disable the sync-on-auth option | + +--- + +## Prerequisites + +- PingOne environment created with at least one population (auto-created as "Default Population") +- For LDAP Gateway: service account in the target directory; gateway agent host meeting the hardware requirements above; outbound WSS network path confirmed +- For External IdP: app registration completed with the provider (client ID and secret or SAML metadata available) +- Administrator role: Environment Admin for gateway and IdP configuration; Identity Admin for population and user management + +--- + +## Common variants + +**Multiple populations with scoped admins:** Create one population per business unit. Assign Identity Admin roles scoped to individual populations. Admins in each population cannot see or modify users in other populations. + +**Hybrid directory model:** PingOne Directory for new cloud users alongside an LDAP Gateway for existing on-premises users. Both populations coexist in the same environment; applications allow both populations. + +**Social + enterprise federation:** External IdP (corporate SAML/OIDC) for employees, Google/Apple social for customers. Each IdP targets a different population; applications allow only the relevant population. + +**LDAP read-through without migration:** Disable migration on first auth. Users authenticate against LDAP indefinitely; no PingOne user record is created. Suitable when the LDAP directory must remain the sole system of record. + +--- + +## Related references + +- `references/curated/pingone-mt/tenant-and-environment-setup.md` — environment creation and initial configuration +- `references/curated/pingone-mt/sign-on-policies.md` — configure authentication policies that reference populations +- `references/curated/cross-platform/foundation-overview.md` — cross-platform identity concepts + +--- + +## Source + +- https://docs.pingidentity.com/pingone/directory/p1_aboutusers.html +- https://docs.pingidentity.com/pingone/directory/p1_populations.html +- https://docs.pingidentity.com/pingone/directory/p1_groups_vs_populations.html +- https://docs.pingidentity.com/pingone/integrations/p1_ldap_gateways.html +- https://docs.pingidentity.com/pingone/integrations/p1_add_ldap_gateway.html +- https://docs.pingidentity.com/pingone/integrations/p1_add_a_user_type.html +- https://docs.pingidentity.com/pingone/integrations/p1_external_idps.html +- https://docs.pingidentity.com/pingone/directory/p1_user_attributes.html +- https://docs.pingidentity.com/pingone/directory/p1_groups.html +- https://docs.pingidentity.com/pingone/directory/p1_groups_vs_populations.html diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/sign-on-policies.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/sign-on-policies.md new file mode 100644 index 0000000..5688083 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/sign-on-policies.md @@ -0,0 +1,228 @@ +--- +title: "PingOne MT — Sign-On Policies and MFA Configuration" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/authentication/p1_authenticationpolicies.html" +--- + +# PingOne MT — Sign-On Policies and MFA Configuration + +Configuring authentication (sign-on) policies and MFA device policies in PingOne MT to control how users prove identity at sign-in. + +## Scope + +**Covers:** Authentication policy structure, action types and their conditions, MFA device policy configuration, step-up authentication, risk-based MFA conditions (Protect integration), policy attachment to applications, and common configuration gotchas. +**Does NOT cover:** DaVinci flow design — see `skills/ping-orchestration/SKILL.md`. PingOne Protect service setup (risk predictors, risk policy scoring) — see `skills/ping-universal-services/SKILL.md`. PingOne ST journey-based policy — see `skills/ping-orchestration/SKILL.md`. FIDO2 device registration flows. + +--- + +## Sign-on policy structure + +An authentication policy is an ordered sequence of **actions** (steps) evaluated top-to-bottom at sign-in. Each action executes in sequence. Conditions on an action determine whether it runs; if no condition matches, the action is skipped or enforced depending on action type. + +Key constraints: +- The first action in a policy cannot carry conditions. If you reorder and promote the second action to position 1, its conditions are automatically stripped. +- Up to 20 policies can be assigned per application. +- Policies execute in listed order — all assigned policies are evaluated, not just the first. + +--- + +## Action types + +| Action | What it does | When to add it | +|---|---|---| +| Login | Collects username and password (or identifier). Always the first action. | Required for all password-based flows. Cannot be omitted if users authenticate with credentials. | +| Multi-Factor Authentication | Prompts for a second factor. Can be conditional (trigger only when risk or context conditions match). | Any flow requiring step-up or mandatory MFA. References an MFA device policy. | +| Identity Verification | Invokes PingOne Verify to verify a government ID or biometric. Requires PingOne Verify add-on license. | High-assurance onboarding or re-verification flows. Cannot be the first action. | +| Progressive Profiling | Prompts authenticated users for additional attributes (e.g., phone number, address). | Post-registration attribute collection. Cannot be the first action. | +| Agreement | Presents terms of service or consent text. User must accept to proceed. | Compliance consent flows. Cannot be the first action. | +| External Identity Provider | Delegates authentication to a configured external IdP (SAML, OIDC, social). | Federated login; social sign-in. | +| Identifier First | Identifies the user before presenting auth options. Used for home-realm discovery. | Multi-IdP environments where IdP is determined by domain or attribute. | + +--- + +## MFA action conditions + +Conditions on the MFA action determine when MFA is triggered. Conditions combine with Boolean OR — any matching condition triggers the MFA step. + +| Condition | License requirement | +|---|---| +| Last sign-on older than N hours | None | +| IP address outside allowed CIDR range | None | +| User belongs to a specific population | None | +| User attribute matches a value (e.g., postal code) | None | +| IP reputation is high risk | PingOne Protect or Passwordless | +| Geovelocity anomaly detected | PingOne Protect or Passwordless | +| Anonymous network detected (VPN / Tor / proxy) | PingOne Protect or Passwordless | + +When no conditions are set, the MFA action always runs for every sign-in. + +--- + +## MFA device policy configuration + +An MFA device policy is a separate, environment-scoped object that the MFA action references. Creating the policy alone does not enable MFA — the policy must be selected inside the MFA action of a sign-on policy, and the sign-on policy must be attached to an application. + +### Allowed MFA methods + +| Method | Environment | Key notes | +|---|---|---| +| Authenticator app (TOTP) | Both | Standard TOTP; any authenticator app supported | +| Email OTP | Both | OTP valid 30 min; SMTP sender configured in Settings > Notification Senders | +| SMS OTP | Customer | OTP valid 30 min; requires Twilio, Syniverse, or custom gateway; virtual numbers not supported | +| Voice OTP | Customer | OTP valid 30 min; China disabled by regulation | +| FIDO2 / Passkeys | Both | Requires a FIDO policy object; legacy FIDO2 devices require separate migration | +| Mobile push (PingID app) | Workforce | Biometrics, push with number matching, offline OTP | +| Desktop (PingID app) | Workforce | Windows/macOS passwordless | + +### Top-level MFA policy fields + +| Field | Options | Notes | +|---|---|---| +| Method selection | User selects default / Prompt user to select / Always display (Workforce) | Controls which method the UI presents first | +| Notification policy | Default or named policy | Controls delivery channel priority for OTP messages | +| New device pairing notification | No notification / Email then SMS / SMS then Email | Alert sent to user when a new device is paired | +| Skip user lock verification | Checkbox | Bypasses lockout check during device pairing | +| Remember device | Off / 1 hour – 90 days | Reduces friction on trusted devices; see gotchas for geo restrictions | + +### Device pairing and enrollment + +| Setting | Options | Notes | +|---|---|---| +| Allow pairing | Enabled / Disabled | Disable to block new device registrations (e.g., freeze enrollment after provisioning) | +| Auto enrollment | On / Off | Triggers enrollment at sign-in if user has no enrolled devices | +| Auto enrollment bypass | Must set MFA action "None or Incompatible Methods" → Bypass | Required for auto-enrollment to work with zero devices | +| Admin pre-provisioning | Via PingOne API or DaVinci | Device registered by admin before user first logs in | + +### Bypass codes + +Admin-generated single-use codes that bypass MFA for locked-out or unenrolled users. Issued via the PingOne API or admin console per-user. Expire after single use or a configurable TTL. + +### FIDO2 / passkey policy fields + +| Field | Options | Notes | +|---|---|---| +| Discoverable credentials | Discouraged / Preferred (default) / Required | Must be Required for usernameless / passkey flows | +| User verification | Discouraged / Preferred (default) / Required | Must be Required for usernameless flows | +| Authenticator attachment | Both (default) / Platform / Cross-platform | Platform = device biometrics; Cross-platform = hardware security key | +| Relying party ID | PingOne default / Custom domain / Manual | Must match the app's origin exactly; sandbox allows localhost | +| Device aggregation | Yes / No | Yes collapses multiple FIDO keys into one listed method | + +--- + +## Step-up authentication + +Step-up enforces stronger authentication for sensitive resources without requiring a full re-login. + +### Per-application policy (native PingOne) + +Attach a stricter sign-on policy (with an unconditional MFA action) to the sensitive application. The less-sensitive app uses a weaker policy. Token claims `acr` and `auth_time` carry the satisfied policy identifier and authentication timestamp. + +### Claim-based step-up (API / resource server pattern) + +1. User authenticates at basic level; access token includes `acr` and `auth_time`. +2. Resource server checks whether `acr` and `auth_time` meet its requirements. +3. If insufficient, resource server returns `401` with challenge header: + ``` + WWW-Authenticate: Bearer error="insufficient_user_authentication", + acr_values="strong_authentication_policy", max_age=300 + ``` +4. Client re-authorizes with `acr_values` and `max_age` on the authorization request: + ``` + GET /{envId}/as/authorize?client_id=&scope=secret + &acr_values=strong_authentication_policy&max_age=300 + ``` +5. User completes the stronger sign-on policy; a new access token is issued. + +Standards: RFC 9470 (OAuth 2.0 Step-Up Authentication), RFC 6750 (Bearer Token). + +### Risk-based step-up (requires PingOne Protect) + +Set the MFA action condition to trigger on "IP reputation is high risk", "Geovelocity anomaly", or "Anonymous network". Risk is evaluated per sign-in; MFA is only triggered when the risk threshold is met. See MFA action conditions table above for license requirements. + +--- + +## Policy attachment to applications + +A sign-on policy has no effect until explicitly attached to an application. Unattached applications fall back to the **environment default authentication policy**. + +| Constraint | Detail | +|---|---| +| Maximum policies per app | 20 | +| Execution order | Policies run in listed order; drag to reorder | +| DaVinci vs. PingOne policies | Mutually exclusive per application — cannot mix native PingOne policies and DaVinci flow policies on the same app | +| Admin console application | Cannot be assigned a custom policy; system policy is fixed | +| Environment default policy | Applies to all apps with no explicit policy assignment; may not enforce MFA — always verify | + +**API for policy assignment:** +``` +POST https://api.pingone.com/v1/environments/{envId}/applications/{appId}/signOnPolicyAssignments +``` + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| MFA policy not referenced in MFA action | MFA action is present in policy but user is never prompted for a second factor | Open the MFA action settings and confirm an MFA device policy is selected | +| Policy not attached to application | Environment default policy used silently; may skip MFA | Always explicitly attach the sign-on policy to each application | +| Auto enrollment not triggering | User with no devices reaches MFA action and it fails or errors | Set the MFA action "None or Incompatible Methods" option to Bypass; confirm auto enrollment is on in MFA policy | +| FIDO2 not completing on mobile or web | Registration or assertion fails with origin mismatch | FIDO2 relying party ID must exactly match the application's domain; check for scheme or path mismatches | +| Usernameless / passkey flow fails | Credential not found or user verification error | Set FIDO policy Discoverable Credentials = Required and User Verification = Required | +| Protect-licensed MFA conditions not visible | IP reputation, geovelocity, anonymous network conditions absent from MFA action condition list | Environment requires PingOne Protect or Passwordless license; verify service activation | +| Remember device unavailable in Singapore | Feature unavailable in Singapore region for PingOne Customer | Use DaVinci flow with explicit session management instead | +| MFA lockout settings not in MFA policy | Cannot find lockout threshold in MFA device policy | Lockout lives in Authentication > MFA Settings, not in the MFA device policy | +| Legacy FIDO2 devices fail on new MFA policy | Existing FIDO2 security key or biometrics users cannot authenticate | Run the FIDO2 legacy device migration before switching to the new MFA policy | +| Risk mitigations not auto-enforced | Risk policy recommends "block" but users are not blocked | Risk mitigations are advisory; wire the recommended action into DaVinci or auth policy conditions explicitly | + +--- + +## Prerequisites + +- PingOne environment with at minimum an Identity Authentication license +- Admin role: Environment Admin or Organization Admin +- For risk-based MFA conditions: PingOne Protect or Passwordless license activated on the environment +- For FIDO2: FIDO policy object created before assigning to MFA device policy +- For PingID push (Workforce): PingID service activated and adapter version 2.17+ (for Remember Me) + +--- + +## Common variants + +| Variant | Pattern | +|---|---| +| Workforce MFA (PingID push) | MFA device policy enables Mobile (PingID app) and Desktop (PingID app); method selection = "User selected default"; remember device up to 90 days | +| Customer CIAM MFA (low friction) | Email OTP or SMS OTP primary; FIDO2 optional; auto enrollment on; "None or Incompatible Methods" = Bypass with enrollment redirect | +| High-assurance CIAM | FIDO2 Required + User Verification Required; no SMS/voice as fallback; separate strict sign-on policy attached only to sensitive apps | +| Step-up for API resources | Basic policy on standard apps; strict policy (unconditional MFA) on API resource app; resource server checks `acr` claim and issues 401 challenge | +| Risk-adaptive MFA | Protect license required; MFA action conditioned on IP reputation / geovelocity / anonymous network; low-risk sessions skip MFA entirely | +| DaVinci orchestrated flow | Attach a DaVinci flow policy instead of a native sign-on policy; cannot coexist with native policies on same app | +| Registration policy (self-service sign-up) | Create a sign-on policy with a Login action; enable the "Enable Registration" checkbox; select the target population — users can self-register at sign-in time. Attach this policy to the registration application. Separate from the sign-in policy; attach each to its respective app. | +| Passwordless SMS OTP | Sign-on policy with a Multi-Factor Authentication action (no Login action); SMS method enabled in the MFA device policy; attach to the passwordless app; users authenticate with a one-time passcode delivered by SMS | + +--- + +## Related references + +- `references/curated/pingone-mt/tenant-and-environment-setup.md` +- `references/curated/cross-platform/policy-and-branding-basics.md` +- `references/curated/cross-platform/foundation-overview.md` + +--- + +## Source + +[PingOne authentication policies](https://docs.pingidentity.com/pingone/authentication/p1_authenticationpolicies.html) +[PingOne MFA device policies](https://docs.pingidentity.com/pingone/authentication/p1_mfa_policies.html) +[PingOne FIDO policies](https://docs.pingidentity.com/pingone/authentication/p1_fido_policies.html) +[PingOne step-up authentication for APIs](https://docs.pingidentity.com/pingone/authentication/p1_stepup_authentication_for_apis.html) +[PingOne Protect risk policies](https://docs.pingidentity.com/pingone/threat_protection_using_pingone_protect/p1_protect_risk_policies.html) +[Attach authentication policy to application](https://docs.pingidentity.com/pingone/applications/p1_apply_auth_policy_to_applications.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md new file mode 100644 index 0000000..7af797b --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md @@ -0,0 +1,171 @@ +--- +title: "PingOne MT — Tenant and Environment Setup" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingone/platformconsole/p1_c_environments.html" +--- + +# PingOne MT — Tenant and Environment Setup + +Provisioning and initial configuration for PingOne MT environments, including environment types, service activation, and key pre-app settings. + +## Scope + +**Covers:** PingOne MT organization structure, environment types, service activation, populations, and pre-app configuration. +**Does NOT cover:** On-prem server installation — see `references/curated/cross-platform/core-admin-patterns.md`. PingOne ST tenants — see `references/curated/cross-platform/tenant-and-environment-setup.md`. + +--- + +## Organization and environment model + +``` +PingOne Organization +└── Environment (one or more per org) + ├── Population (user store) + ├── Applications (OIDC, SAML, Worker) + ├── Connections (external directories, identity providers) + ├── Sign-on Policies + └── Services (MFA, Verify, DaVinci, Risk, Credentials) +``` + +**Organization:** Top-level billing and admin unit. One organization per PingOne MT customer. + +**Environment:** Logical container for a project, workload, or deployment stage (dev, staging, prod). All resources are environment-scoped. + +--- + +## Environment types + +| Type | SLA | Intended use | +|---|---|---| +| Sandbox | Best-effort | Isolated experimentation; no production data | +| Development | Best-effort | Feature development and integration testing | +| Production | SLA-backed | Live user traffic; highest availability | + +Environment type is immutable after creation. Provision separate environments for each stage. + +--- + +## Creating a new environment + +**Required fields:** + +| Field | Notes | +|---|---| +| Environment name | Human-readable identifier; not user-visible | +| Environment type | Sandbox, Development, or Production | +| Region | Affects data residency; cannot be changed after creation | +| License | Select which org license to use for this environment | +| Services | Select services to activate (MFA, Verify, DaVinci, Risk, Credentials, IGA) — can be added later | +| Generate sample populations and users | Optional (Sandbox only) — adds 2 sample populations and 40 sample users for testing; useful for hands-on exploration | + +**API:** `POST /v1/environments` + +**Service setup after creation:** Some products (e.g., PingFederate) require additional deployment steps after the environment is created. On the environment's Overview page, locate a grayed-out service and expand View Setup Instructions to complete setup. Cloud products are best deployed via Docker images (see Ping Identity DevOps site). PingOne services activate automatically at environment creation. + +--- + +## Key settings to configure before adding apps or users + +| Setting | Location | Notes | +|---|---|---| +| Custom domain | Settings → Custom Domains | Required for branded hosted pages; DNS CNAME to Ping | +| Notification sender | Settings → Notifications | Email/SMS sender; verify domain ownership | +| Default sign-on policy | Policies → Sign-on | Applied to all apps unless overridden per app | +| Admin population | Directory → Populations | Create before provisioning admin users | + +--- + +## MFA policy setup (PingOne MT) + +MFA policies are environment-scoped and control which authentication methods are permitted. + +**Required decisions before configuring:** +- Which MFA methods to allow: TOTP authenticator, SMS OTP, email OTP, FIDO2/passkeys, push notification +- Default enrollment policy: optional, required on next login, or required immediately +- Grace period: number of days before enforcement after policy is applied + +**MFA policy attachment:** A sign-on policy's MFA action references the MFA device policy. The policy must be assigned to the sign-on policy to take effect — creating the policy alone does not enable MFA. + +**Admin surface:** Policies → MFA → + Add MFA Device Policy + +--- + +## OIDC application (client credentials, M2M) + +For a backend service using client credentials grant: + +| Field | Notes | +|---|---| +| Application type | Worker (M2M) or OIDC Web App with Client Credentials grant | +| Grant type | Client Credentials only — no redirect URI needed | +| Token endpoint | `https://auth.pingone.com/{envId}/as/token` | +| Scopes | Minimum: none required; add resource scopes as needed | +| Client authentication | Client Secret Basic or Client Secret Post | + +**Token endpoint authentication:** POST with `grant_type=client_credentials&client_id=&client_secret=` (for `client_secret_post`) or HTTP Basic header (for `client_secret_basic`). + +--- + +--- + +## Service activation reference + +Services must be explicitly activated per environment before flows can use them. Activation is not inherited from other environments. + +| Service | License tier | Activation effect | +|---|---|---| +| SSO | Base | Always active; cannot be disabled | +| MFA | Base or add-on | Enables MFA methods in sign-on policies and MFA device policies | +| DaVinci | Add-on | Enables DaVinci flow designer and DaVinci policy attachment to apps | +| Risk (PingOne Protect) | Add-on | Enables risk predictors, risk policies, and risk-based MFA conditions | +| Verify | Add-on | Enables PingOne Verify identity proofing actions in sign-on policies | +| Credentials | Add-on | Enables verifiable credential issuance and revocation | +| IGA | Add-on (separate onboarding) | Requires additional provisioning step by Ping Identity support | + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Environment type immutable | Created sandbox instead of production | Provision a new environment with the correct type; no conversion path | +| Region immutable | Environment created in wrong region | Provision a new environment; migrate config and users | +| Service not activated | DaVinci flow designer not visible; risk conditions absent from policy editor | Activate the service in the environment's service management settings | +| Multiple populations on one app | Users in secondary population denied access | Add all relevant populations to the app's Allowed Populations list | +| Default policy changes affect all apps | Unintended behavior change after policy edit | Explicitly attach named policies to each app; avoid relying on the environment default | +| Worker app secret not saved at creation | Cannot retrieve secret later; all API calls fail | Rotate the secret immediately; copy the new value before closing the dialog | + +## Prerequisites + +- PingOne organization account with admin access +- Admin role assigned: Environment Admin or Organization Admin +- DNS control (for custom domain configuration) + +## Common variants + +| Variant | Note | +|---|---| +| Multiple environments per org | Supported; each environment is isolated; use environment clone for faster setup of staging/prod | +| Worker application for API automation | Create a Worker app with client credentials; assign admin roles to the worker app for API-driven config | +| Populations for multi-tenant use cases | Create separate populations to segment user groups; policies and apps can target specific populations | + +## Related references + +- `references/curated/cross-platform/foundation-overview.md` +- `references/curated/cross-platform/tenant-and-environment-setup.md` +- `references/curated/cross-platform/policy-and-branding-basics.md` + +## Source + +[PingOne environments](https://docs.pingidentity.com/pingone/platformconsole/p1_c_environments.html) +[PingOne MFA device policies](https://docs.pingidentity.com/pingone/mfa/p1_c_mfa_device_policies.html) +[PingOne OIDC application setup](https://docs.pingidentity.com/pingone/platformconsole/p1_c_apps.html) +[PingOne API reference](https://apidocs.pingidentity.com/pingone/platform/v1/api/) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md new file mode 100644 index 0000000..9044cbd --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md @@ -0,0 +1,253 @@ +--- +title: "PingOne MT — Themes, Branding, and Notifications" +product_family: pingone-mt +products: ["pingone", "davinci"] +capabilities: ["foundation", "branding"] +services: [] +audience: ["admin", "developer"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/branding/p1_branding.html" +--- + +# PingOne MT — Themes, Branding, and Notifications + +UI customization for PingOne MT hosted pages and DaVinci-hosted flow pages: branding assets, custom domains, notification templates, and DaVinci UI Studio. + +## Scope + +**Covers:** PingOne hosted-page branding (logo, colors, custom domain, error pages); email and SMS notification templates and senders; DaVinci UI Studio for flow pages; CSP/font constraints; multi-environment branding strategy. + +**Does NOT cover:** PingOne ST theming — see `references/curated/pingone-st/themes-and-customization.md`. DaVinci flow logic — see `ping-orchestration`. End-app UI customization — see `ping-app-integration`. + +--- + +## Branding scope — what you can theme in PingOne MT + +| Surface | Themed via | Notes | +|---|---|---| +| PingOne hosted sign-on / consent pages | Branding admin (Settings → Branding) | Logo, colors, favicon, footer text | +| PingOne self-service app | Same Branding admin | Inherits the environment's branding | +| Email notification templates | Email Templates admin | HTML editor with merge variables; per-environment | +| SMS notification templates | SMS Templates admin | Plain text with merge variables | +| DaVinci-hosted flow pages | DaVinci UI Studio | Per-flow or per-tenant DaVinci theme; separate from PingOne branding | +| End-app login UI (rendered in your app) | Your application code via Ping SDK | Not branded by Ping; use SDK collectors | + +**Critical:** PingOne hosted-page branding and DaVinci-hosted page branding are **separate systems**. A flow that redirects from PingOne to DaVinci will show two different branded surfaces unless both are configured to match. + +--- + +## PingOne branding configuration + +**Admin surface:** PingOne admin console → Settings → Branding + +| Field | Notes | +|---|---| +| Logo | PNG or SVG; max 2 MB; appears in headers of all hosted pages | +| Brand colors | Primary, secondary, link, button — hex values applied via CSS variables | +| Favicon | PNG or ICO; appears in browser tabs | +| Footer text | Optional; supports basic HTML for legal/privacy links | +| Background image | Optional; full-page background on consent and confirmation pages | +| Font | Web-safe stack only; custom CDN fonts require CSP update (see below) | + +**Per-environment:** Each PingOne environment has its own branding. Set Dev / Staging / Production environments separately. Trial and Sandbox environments default to Ping branding until customized. + +--- + +## Custom domains + +Required for branded hosted pages — without a custom domain, hosted pages render at `auth.pingone.com` (Ping's neutral domain). + +**Admin surface:** Settings → Custom Domains → Add Custom Domain + +| Step | Notes | +|---|---| +| Add domain | e.g., `id.example.com` | +| DNS verification | Add the CNAME record provided by PingOne; verify before proceeding | +| TLS certificate | PingOne automatically provisions a certificate via Let's Encrypt or accepts a customer-provided certificate | +| Domain activation | After verification + cert issuance, hosted pages render at the custom domain | + +**Constraint:** When you switch to a custom domain, **all redirect URIs registered against existing applications must be updated** to use the new hostname. The change is not automatic. + +--- + +## Email notification templates + +**Admin surface:** Settings → Email Templates + +PingOne sends transactional emails for: account creation, password reset, MFA OTP, identity verification, account locked, and custom journey notifications. + +| Field | Notes | +|---|---| +| Subject line | Supports merge variables (e.g., `${user.username}`) | +| Body (HTML) | Full HTML editor; embedded images must be hosted at an externally accessible URL | +| Plain-text fallback | Required for deliverability; auto-generated from HTML if blank | +| Locale | Multiple template variants per locale; PingOne selects based on `Accept-Language` | + +**Merge variables:** + +| Variable | Source | +|---|---| +| `${user.username}` | User identity store | +| `${user.name.given}` | User profile | +| `${user.email}` | User profile | +| `${otp}` | Generated for OTP/verification templates | +| `${recoveryCode}` | Generated for password reset | +| `${magicLink}` | Generated for magic link sign-in flows | +| `${environment.name}` | PingOne environment metadata | + +--- + +## Email senders + +**Admin surface:** Settings → Notifications → Senders + +| Sender option | Notes | +|---|---| +| PingOne default | `noreply@pingidentity.com` — works out of box; no domain verification | +| Verified custom domain | Required for branded sender (e.g., `noreply@id.example.com`); requires SPF + DKIM + DMARC records | +| Third-party SMTP | Configure outbound relay through SendGrid, Mailgun, etc. — use for high-volume environments | + +**Production recommendation:** Always verify a custom sender domain before go-live. Unverified senders use Ping's neutral domain; users see "noreply@pingidentity.com" instead of your brand. + +--- + +## SMS notification templates + +**Admin surface:** Settings → SMS Templates + +| Field | Notes | +|---|---| +| Body (plain text) | Limited to 160 characters per segment; longer messages are split | +| Merge variables | Same set as email templates | +| Locale | Multiple variants supported | + +**SMS sender configuration:** Settings → Notifications → SMS Provider — Twilio is the default; alternative providers configurable per environment. + +--- + +## DaVinci UI Studio + +DaVinci flows that use HTML Template nodes render at DaVinci-hosted URLs. UI Studio is the visual editor for these page templates. + +**Admin surface:** DaVinci console → UI Studio + +| Capability | Notes | +|---|---| +| Per-flow themes | Each flow can use a different theme; useful for multi-brand tenants | +| Tenant-default theme | Falls back to this theme when a flow does not specify one | +| Custom HTML/CSS | Full HTML editor; images can be uploaded to DaVinci asset storage or referenced externally | +| Form components | Pre-built component library (input, button, link, error message, etc.) | +| Variable binding | Bind component fields to DaVinci flow variables | + +**Important:** UI Studio themes apply to **DaVinci-hosted pages only**. They do NOT affect PingOne-hosted pages (which use PingOne branding). Both systems must be configured if a flow redirects between them. + +--- + +## Custom CSS / fonts and CSP + +PingOne hosted pages enforce a Content Security Policy. + +| Asset type | Where to allow | +|---|---| +| External fonts (Google Fonts, custom CDN) | Settings → Domains and Branding → Content Security Policy → `font-src` | +| External images (in email templates) | No CSP restriction (email is rendered by the recipient's mail client) | +| External CSS overrides | Not supported — PingOne does not allow custom CSS injection on hosted pages; use brand colors and logos instead | +| Inline styles in DaVinci HTML Template | Allowed; DaVinci pages have a separate CSP managed in DaVinci settings | + +**Hard limit:** PingOne hosted pages do NOT support customer-supplied CSS. If you need pixel-level control, host the page in your own application using the Ping SDK (see `ping-app-integration`). + +--- + +## Branding strategy patterns + +### Pattern A — Single brand, all environments +- One custom domain, one logo, one color set +- Apply identical branding to Dev, Staging, Production environments +- Best for: workforce identity; single-product CIAM + +### Pattern B — Per-environment differentiation +- Distinct logo or color treatment for Dev (e.g., yellow border) vs Production +- Helps prevent operator confusion (don't accidentally make changes in Production) +- Same custom domain pattern: `id-dev.example.com`, `id.example.com` + +### Pattern C — Multi-brand single tenant +- Separate PingOne environments per brand +- Each environment has its own custom domain, branding, email templates +- Each application is registered in the brand's environment +- Best for: parent companies with distinct consumer brands; B2B2C platforms + +### Pattern D — Co-existing PingOne + DaVinci surfaces +- Configure PingOne branding to match +- Configure DaVinci UI Studio theme to use the same logo/colors/fonts +- Verify visual continuity by walking the user-facing flow end-to-end before go-live + +--- + +## Pre-go-live branding checklist + +| Item | PingOne MT | DaVinci | +|---|---|---| +| Custom domain configured + DNS verified | Yes — Settings → Custom Domains | N/A (DaVinci uses its own subdomain) | +| TLS certificate active | Yes — auto-provisioned or customer-uploaded | Yes — DaVinci-managed | +| Logo uploaded | Settings → Branding | UI Studio → Theme | +| Brand colors set | Settings → Branding | UI Studio → Theme | +| Favicon uploaded | Settings → Branding | Not applicable for DaVinci hosted pages | +| Email sender verified | Settings → Notifications → Senders | DaVinci uses PingOne Notifications connector — verified at PingOne level | +| Email templates customized | Settings → Email Templates | N/A — DaVinci uses PingOne Notifications connector | +| SMS templates customized | Settings → SMS Templates | Same as email | +| Hosted page branding tested | Walk a sign-on flow through hosted pages | Walk the DaVinci flow end-to-end | +| CSP entries for any custom fonts | Settings → CSP → `font-src` | UI Studio → Theme settings | + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Logo shows but colors don't apply | Cached CSS in browser | Hard refresh; CDN cache TTL is 5 minutes globally | +| Custom font fails to load | Falls back to web-safe font | Add font origin to CSP `font-src` | +| Emails arrive from `noreply@pingidentity.com` despite custom sender | Sender domain not verified | Complete SPF + DKIM verification at Settings → Notifications → Senders | +| DaVinci flow looks unbranded after PingOne redirect | DaVinci theme not configured | Set tenant-default theme in DaVinci UI Studio; assign theme to flow | +| Custom domain redirect URI mismatch | Apps fail with `redirect_uri_mismatch` after domain switch | Update each app's redirect URIs to use the new custom domain hostname | +| HTML email template breaks in Outlook | Modern CSS (flexbox, grid) doesn't render | Use table-based layouts and inline CSS for email; test with Litmus or similar | +| Trial environment shows trial badge in branding | Cannot remove via Branding admin | Trial badge is enforced; go to a Production environment for full branding | + +--- + +## Prerequisites + +- Environment Admin role on the target PingOne environment +- For custom domains: DNS administrative access to the domain +- For verified senders: DNS administrative access to add SPF, DKIM, DMARC records +- For DaVinci UI Studio: DaVinci admin role; DaVinci service activated on the environment + +--- + +## Common variants + +| Variant | Note | +|---|---| +| Workforce default | Use environment branding only; no DaVinci flows | +| CIAM with DaVinci | Configure both PingOne and DaVinci themes; verify visual continuity | +| Multi-brand CIAM | One PingOne environment per brand; separate domains, branding, email senders | +| Hybrid PingOne + on-prem | Customer's web app (or SDK) handles its own branding; PingOne branding only matters for hosted-page redirects | + +--- + +## Related references + +- `references/curated/pingone-mt/tenant-and-environment-setup.md` — environment provisioning +- `references/curated/cross-platform/policy-and-branding-basics.md` — branding overview +- `references/curated/pingone-st/themes-and-customization.md` — PingOne ST theming (separate platform) + +## Source + +- [PingOne branding](https://docs.pingidentity.com/pingone/branding/p1_branding.html) +- [PingOne custom domains](https://docs.pingidentity.com/pingone/custom_domains/p1_custom_domains.html) +- [PingOne email templates](https://docs.pingidentity.com/pingone/notifications/p1_notifications_email_templates.html) +- [PingOne notifications](https://docs.pingidentity.com/pingone/notifications/p1_notifications.html) +- [DaVinci UI Studio](https://docs.pingidentity.com/davinci/davinci_uistudio/davinci_uistudio_overview.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md new file mode 100644 index 0000000..18f4f20 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md @@ -0,0 +1,154 @@ +--- +title: "PingOne ST — App Setup" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "developer"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-create_oauth2_client.html" +--- + +# PingOne ST — App Setup + +Register OIDC, OAuth 2.0, and SAML applications in PingOne ST so they can authenticate users through journeys. + +## Scope + +**Covers:** OIDC client registration, OAuth 2.0 client setup, SAML SP/IdP registration, key configuration fields. +**Does NOT cover:** Journey design — see `references/curated/pingone-st/authentication-fundamentals.md`. Provisioning to external systems — see `references/curated/pingone-st/directory-setup.md`. + +--- + +## Application types + +| Type | When to use | +|---|---| +| OIDC / OAuth 2.0 client | Web apps, SPAs, mobile apps, M2M service accounts needing tokens | +| SAML SP | Enterprise apps using SAML 2.0 federation | +| SAML IdP | PingOne ST acting as IdP to a third-party SP | + +--- + +## OIDC / OAuth 2.0 application + +**Admin surface:** AIC tenant admin console → Applications → OAuth 2.0 Clients → + Create Client + +**Required configuration:** + +| Field | Notes | +|---|---| +| Client ID | Auto-generated or custom; must be unique within the realm | +| Client Secret | Required for confidential clients; omit for public (SPA, native) | +| Redirect URIs | Exact match enforced; add all environments upfront to avoid `redirect_uri mismatch` in lower envs | +| Grant Types | Authorization Code (web apps), Client Credentials (M2M), Refresh Token as needed; avoid Implicit | +| Scopes | Minimum: `openid`; add `profile`, `email`, `address`, `phone` as needed | +| Client Type | Confidential (can hold a secret) or Public (SPA/native; requires PKCE) | + +**Client authentication methods:** +- `client_secret_basic` — HTTP Basic header; most common for confidential clients +- `client_secret_post` — client ID/secret in POST body +- `private_key_jwt` — JWT signed with client's private key; recommended for high-security M2M + +**OIDC discovery endpoint:** +`https:///am/oauth2/realms/root/realms//.well-known/openid-configuration` + +**Token options:** +- Access token format: server-side (stateful) or JWT (stateless) +- ID token encryption: optional; configure if the client cannot inspect the JWT +- Refresh token expiry: per-client override or realm default + +--- + +## SAML application + +**Admin surface:** AIC admin console → Applications → SAML Applications → + Register Application + +**Required configuration:** + +| Field | Notes | +|---|---| +| Entity ID | Unique SP identifier; typically the app's base URL or a URN | +| ACS URL | Assertion Consumer Service URL — where PingOne ST POST-binds the SAML response | +| Single Logout URL | Optional; required for SLO support | +| Name ID Format | `email`, `persistent`, or `transient` — dictated by SP requirements | +| Signing | Enable response and/or assertion signing; export IdP metadata to share with SP admin | + +**Metadata exchange:** Import SP metadata XML if available to auto-populate ACS URL, entity ID, and certificates. Export PingOne ST IdP metadata from Applications → SAML Applications → (app) → Export Metadata. + +--- + +## Assigning a journey to an application + +An OIDC or SAML app uses the realm's default authentication journey unless overridden. + +**Override location:** Application settings → Authentication → Journey + +Use per-app journey assignment to serve distinct login experiences (e.g., workforce vs. customer, standard vs. high-assurance) without modifying the realm default. + +--- + +## Provisioning from apps + +Applications can provision user accounts to 40+ external systems (Salesforce, Workday, Active Directory, Microsoft Entra ID) using PingIDM connectors. + +**Admin surface:** Applications → Provisioning + +Configure provisioning after basic app setup is complete and the identity store is verified. + +--- + +--- + +## Token and session configuration + +| Setting | Applies to | Constraint | +|---|---|---| +| Access token lifetime | OIDC clients | Configured per client; default varies by realm; shorter is better for API security | +| Refresh token rotation | OIDC clients with offline_access | Rotating refresh tokens reduce replay risk; configure grace period to avoid revocation on concurrent use | +| ID token claims | OIDC clients | Claims populated from mapped identity store attributes; map attributes under Application → Claims | +| PKCE enforcement | Public clients | Required for SPA and native apps; enforced by setting Client Type = Public | +| Token blacklisting | All OIDC clients | PingAM supports stateful access tokens with per-token revocation; enable via OAuth 2.0 provider settings | + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Client registered in wrong realm | `invalid_client` error at token endpoint; client not found | Confirm the realm in the OIDC discovery endpoint matches the realm where the client was created | +| Redirect URI mismatch | `redirect_uri mismatch` error from PingAM | Register the exact URI; trailing slash and path case are significant | +| Journey not activated | Login flow returns error or no response | Activate the journey from the Journey editor before testing | +| Application using realm default journey | Different login experience than expected | Override the journey at Application settings → Authentication → Journey | +| SAML SP metadata imported but entity ID mismatch | SAML assertion delivery fails | Verify entity ID in the imported metadata matches what the SP will send in `AuthnRequest` | +| Token endpoint authentication method mismatch | `invalid_client` on token exchange | Align client's configured auth method with what the SDK or HTTP client sends | + +## Prerequisites + +- PingOne ST tenant with admin access +- Realm configured with at least one identity store (see `references/curated/pingone-st/directory-setup.md`) +- At least one authentication journey ready or in progress (see `references/curated/pingone-st/authentication-fundamentals.md`) + +## Common variants + +| Variant | Note | +|---|---| +| SPA | Public client type, PKCE required, no client secret | +| M2M / service account | Client Credentials grant, no redirect URI needed | +| Realm-scoped clients | Clients registered in `alpha` are not available in `bravo` and vice versa | + +## Related references + +- `references/curated/pingone-st/authentication-fundamentals.md` +- `references/curated/pingone-st/foundation-overview.md` +- `references/curated/pingone-st/directory-setup.md` + +## Source + +[Register OAuth 2.0 clients — PingOne ST](https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-create_oauth2_client.html) +[OIDC client registration](https://docs.pingidentity.com/pingoneaic/am-oidc-guide/oidc-client-registration.html) +[SAML application registration](https://docs.pingidentity.com/pingoneaic/am-saml2-guide/saml2-sp-registration.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md new file mode 100644 index 0000000..c892f09 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md @@ -0,0 +1,151 @@ +--- +title: "PingOne ST — Authentication Fundamentals" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/am-journey-guide/journey-overview.html" +--- + +# PingOne ST — Authentication Fundamentals + +Understand and configure authentication in PingOne ST: the journey model, realm authentication settings, and key design rules before building any login flow. + +## Scope + +**Covers:** Journey/tree concepts, realm authentication settings, node basics, journey design rules. +**Does NOT cover:** Specific node-by-node flow design — that belongs in `ping-orchestration`. Application-to-journey assignment — see `references/curated/pingone-st/app-setup.md`. + +## Key steps / content + +### The journey model + +Authentication in PingOne ST is driven by **journeys** (also called authentication trees in PingAM). A journey is a configurable directed graph of **nodes** connected by outcome branches. + +- Journeys replace static login pages with flexible, branching flows +- Nodes perform a single function: collect a credential, evaluate a condition, invoke a service, set a session variable +- Branches connect nodes to the next step based on the outcome (e.g., `True` / `False`, `Match` / `No Match`, `Success` / `Failure`) +- Journeys can be nested: an inner journey node invokes another journey and passes its result upstream + +**End states:** Every journey must reach one of two terminal nodes: +- `Success` — authentication approved, session created +- `Failure` — authentication denied + +### Realm authentication settings + +**Admin surface:** AIC admin console → Authentication → Journeys (or AM admin console → Realm → Authentication → Settings) + +| Setting | Purpose | +|---|---| +| Default authentication journey | Used when no specific journey is requested (e.g., `/login` with no `authIndexType` parameter) | +| Default failure URL | Where users land after a `Failure` outcome when the client does not specify | +| Default success URL | Where users land after a `Success` outcome when the client does not specify | +| Session settings | Idle timeout, max session time, session quota (max concurrent sessions per user) | + +**Per-application override:** A specific journey can be assigned to an individual application. Set at Application settings → Authentication → Journey. Different apps can present different login flows without modifying the realm default. + +### Node categories and common nodes + +| Category | Examples | +|---|---| +| **Input / collection** | Username Collector, Password Collector, Choice Collector, Attribute Collector | +| **Credential validation** | Data Store Decision (validates username + password against identity store) | +| **MFA / step-up** | WebAuthn Registration/Authentication, OTP Email/SMS, Push Authentication | +| **Conditional logic** | Scripted Decision, Attribute Present Decision, Session Data Decision | +| **Session / profile** | Set Session Properties, Set Persistent Cookie, Profile Completeness Decision | +| **External integrations** | Social Provider Handler, LDAP Decision, HTTP Client, Platform Password | + +**Scripted Decision node:** Executes a custom JavaScript or Groovy script to implement logic not covered by built-in nodes. The script returns a named outcome that maps to a branch. Requires a Decision Node script defined in Scripts. + +**Transient state:** Nodes can store intermediate values in transient state (in-memory, session-duration) or shared state (survives across inner journeys). Use transient state to pass the username collected in one node to a validation node further down the journey. + +### Key design rules + +These are constraints that prevent broken or unexpected behavior: + +| Rule | Why it matters | +|---|---| +| Do not set a journey as both **default** AND **always run** | "Always run" forces re-execution of the journey on every request, including mid-session. Combined with "default," this can create redirect loops. | +| Do not map a journey to the default ACR value if it is set to "always run" | Same loop risk via ACR-based routing. | +| If a user re-authenticates with the same journey during an active session, journey processing is skipped by default | This is intentional session-level caching. Override with `ForceAuth=true` if re-authentication is required. | +| Duplicate journeys via the editor (More → Duplicate) before modifying production journeys | Preserves a working copy. Journeys cannot be version-controlled natively in the console. | + +### Journey activation + +Journeys must be **activated** to be available for authentication. A deactivated journey still exists in the editor but returns an error if invoked. + +**Admin surface:** Journey editor → More (⋮) → Activate / Deactivate + +### How clients invoke a journey + +| Method | When to use | +|---|---| +| Application override | Preferred; set once in app config, no per-request parameters needed | +| OIDC `acr_values` | Append `&acr_values=` to the authorization request | +| Direct AM endpoint | `/login?authIndexType=service&authIndexValue=` — use for testing or legacy integrations | + +--- + +## Journey troubleshooting patterns + +| Symptom | Likely cause | Diagnosis step | +|---|---|---| +| Journey returns error on activation | Node configuration is incomplete or references a deleted script | Open the journey editor; look for nodes with red indicators; check Scripts for the referenced script | +| User reaches `Failure` node unexpectedly | A credential validation node rejected the input (wrong password, locked account, schema mismatch) | Enable debug logging in PingAM (`org.forgerock.openam.auth.nodes` logger) and replay the authentication | +| Journey skipped entirely for authenticated user | Session already exists; journey caching prevents re-execution | Append `ForceAuth=true` to the authorization request to bypass session-level caching | +| Inner journey not executing | Inner journey is deactivated or the Journey node references the wrong name | Verify the inner journey is activated; check the Journey node's `Journey Name` field for exact case-sensitive match | +| `Cannot find node of type X` error | A node type was removed from the product or a module was disabled | Check if the node's plugin is installed and enabled; reinstall or replace the node | +| `Scripted Decision` node always exits on `False` | Script syntax error or unhandled exception | Check the AM server log for `ScriptException` entries; validate the script independently | + +--- + +## Session management + +| Setting | Location | Notes | +|---|---|---| +| Session idle timeout | Realm → Sessions → Max Idle Time | After this period of inactivity the session expires; user must re-authenticate | +| Session max time | Realm → Sessions → Max Session Time | Absolute limit regardless of activity; prevents infinite sessions | +| Session quota | Realm → Sessions → Maximum Sessions | Limits concurrent sessions per user; older sessions destroyed when limit reached | +| Session notifications | AM admin console → Sessions → Notifications | Push session invalidation events to registered listeners (e.g., app server logout) | + +Session tokens issued by PingAM are bound to the realm. A session created in the `alpha` realm is not valid in the `bravo` realm. + +Cross-realm SSO requires explicit federation configuration (e.g., an OAuth2 authorization grant referencing a cross-realm token, or a SAML federation between the two realms). This is an advanced pattern — plan the realm architecture before onboarding users to avoid cross-realm session issues. + +## Prerequisites + +- PingOne ST tenant with at least one realm configured +- Identity store connected to the realm (see `references/curated/pingone-st/directory-setup.md`) +- Admin access to Authentication → Journeys +- Scripts environment configured if Scripted Decision nodes will be used (AIC admin console → Scripts) + +## Common variants + +| Variant | Note | +|---|---| +| Inner journeys | Nest frequently-reused logic (e.g., MFA step) into a reusable inner journey invoked by a Journey node | +| Workforce vs. CIAM | Workforce flows are often simpler (username + password + MFA). CIAM flows add registration, progressive profiling, and verification steps — typically built in `ping-orchestration`. | +| ForgeRock AM auth trees | Same underlying model as PingAM trees. Existing ForgeRock trees can be migrated to PingOne ST journeys with node mapping. | +| ACR-based routing | Different client apps can invoke different journeys by passing `acr_values` on the authorization request; map ACR values to journey names in OAuth 2.0 provider settings | + +## Related references + +- `references/curated/pingone-st/app-setup.md` — assign journeys to OIDC/SAML applications +- `references/curated/pingone-st/foundation-overview.md` — tenant and realm architecture +- `references/curated/pingone-st/directory-setup.md` — identity store configuration required before journey data store decisions + +## Source + +[Journey overview — PingOne ST](https://docs.pingidentity.com/pingoneaic/am-journey-guide/journey-overview.html) +[Authentication nodes reference](https://docs.pingidentity.com/pingoneaic/am-authentication/authentication-node-reference.html) +[Getting started: authentication journey](https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-authentication_journey.html) +[Session management — PingAM](https://docs.pingidentity.com/pingoneaic/am-sessions-guide/session-management-overview.html) +[Scripted decision node — PingAM](https://docs.pingidentity.com/pingoneaic/am-authentication/auth-node-scripted-decision.html) +[Inner journeys — PingAM](https://docs.pingidentity.com/pingoneaic/am-journey-guide/journey-inner-trees.html) +[ACR values and authentication — PingAM](https://docs.pingidentity.com/pingoneaic/am-oidc-guide/oidc-acr-values.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md new file mode 100644 index 0000000..2158860 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md @@ -0,0 +1,159 @@ +--- +title: "PingOne ST — Directory Setup (User Management)" +product_family: pingone-st +products: ["pingone-aic", "pingidm", "pingds"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-identity_store.html" +--- + +# PingOne ST — Directory Setup (User Management) + +Identity store options, schema configuration, and provisioning patterns for PingOne ST. + +## Scope + +**Covers:** PingDS identity store, external LDAP/AD connection, managed objects and schema, provisioning and reconciliation. +**Does NOT cover:** Authentication journey configuration — see `references/curated/pingone-st/authentication-fundamentals.md`. Deep provisioning flow design — see `ping-orchestration`. + +--- + +## Identity store options + +Each realm must be associated with an identity store. + +| Option | When to use | +|---|---| +| PingDS (default) | New deployments; cloud-native, fully managed by Ping Identity | +| External LDAP / AD | Migrating from on-prem directory; AD remains authoritative | +| Multiple stores | Advanced: route different user populations to different stores | + +PingDS requires no additional setup — users created via PingIDM are stored here automatically. + +**Admin surface:** AM admin console → Realm → Identity Stores → + Add Identity Store + +--- + +## External LDAP / Active Directory configuration + +**Required fields:** + +| Field | Example / Notes | +|---|---| +| LDAP server URL | `ldaps://ad.example.com:636` — TLS strongly recommended | +| Bind DN | `uid=pingbind,ou=service,dc=example,dc=com` | +| Bind password | Use a secret/ESV rather than plaintext | +| Base DN | `dc=example,dc=com` | +| User search filter | `(uid=*)` for LDAP; `(sAMAccountName=*)` for AD | +| Group search filter | `(objectClass=groupOfNames)` | + +**Active Directory specifics:** +- Username attribute: `sAMAccountName` (not `uid`) +- Account enable/disable: controlled via `userAccountControl` +- Object identifier: GUID — configure `objectGUID` as the DS object identifier +- Kerberos pass-through auth: supported via LDAP gateway + +**Constraint:** The DS certificate must be shared with the AM container before TLS connectivity will succeed. + +--- + +## Managed objects and user schema (PingIDM) + +**Default managed object types:** + +| Object type | Purpose | +|---|---| +| `user` | End-user identity | +| `role` | Access role assigned to users | +| `assignment` | Maps a role to an entitlement | +| `group` | User group | +| `organization` | Org hierarchy node (B2B / delegated admin) | + +**Critical:** Do not delete default managed objects. Removing them can break the tenant. + +**Schema extension:** +- Custom attributes on `user` must be added via the IDM admin console before they can be set or queried +- **Admin surface:** IDM admin console → Managed Objects → user → Properties → + Add Property +- Properties not defined in schema will not appear in the UI and their sub-properties cannot be configured +- Custom object types (e.g., IoT devices, contracts) are supported + +**Core user properties:** `userName`, `password`, `mail`, `givenName`, `sn`, `telephoneNumber`, `displayName`, `accountStatus` + +--- + +## Provisioning and reconciliation + +PingIDM uses **mappings** to move identity data between systems. + +**Mapping components:** + +| Component | Purpose | +|---|---| +| Source | Where data originates (e.g., `system/ldap/account`) | +| Target | Where data is written (e.g., `managed/alpha_user`) | +| Attribute map | Source attribute → target attribute | +| Conditions | JavaScript expression to control whether the mapping fires (e.g., active accounts only) | +| Transforms | JavaScript function to reshape values (e.g., combine first + last → displayName) | + +**Reconciliation phases:** +1. Source reconciliation — identifies changes in the source +2. Target reconciliation — detects orphaned target objects (handles deletions) + +**LDAP/AD connector key settings:** + +| Setting | Notes | +|---|---| +| `objectClassesToSynchronize` | `inetOrgPerson` for LDAP; `user` for AD | +| `attributesToSynchronize` | Leave empty to sync all; restrict for performance | +| `accountSynchronizationFilter` | LDAP filter to scope which accounts sync | +| `changeLogBlockSize` | Default 100; increase for high-volume directories | + +**Outbound provisioning:** 40+ connectors available (Microsoft Entra ID, Salesforce, Workday, Active Directory, ServiceNow). Admin surface: IDM admin console → Connectors → + New Connector. + +--- + +## User creation methods + +| Method | Notes | +|---|---| +| IDM admin console | Manual; Managed Objects → user → + New User | +| Self-registration journey | User-initiated via authentication journey (see `ping-orchestration`) | +| SCIM inbound | External HR/provisioning system pushes via SCIM 2.0 endpoint | +| Reconciliation | PingIDM pulls from external directory on schedule | +| REST API | `POST /openidm/managed/alpha_user` | + +--- + +## Prerequisites + +- PingOne ST tenant with at least one realm +- For external LDAP/AD: LDAPv3-compliant server; TLS certificate shared with AM container; service account credentials +- For PingDS multi-server replication: same encryption passphrase on all nodes + +## Common variants + +| Variant | Note | +|---|---| +| Alpha realm users | Stored as `managed/alpha_user`; realm prefix is part of the path | +| Multiple realms | Each realm has its own user store path: `bravo_user`, `alpha_user`, etc. | +| Hybrid: PingDS + AD | PingDS as primary; AD connector syncs a subset of attributes on schedule | +| Delegated administration | Use `organization` managed objects to scope admin access to a subset of users | + +## Related references + +- `references/curated/pingone-st/foundation-overview.md` +- `references/curated/pingone-st/authentication-fundamentals.md` +- `references/curated/pingone-st/app-setup.md` + +## Source + +[Identity store setup — PingOne ST](https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-identity_store.html) +[Managed objects — PingIDM](https://docs.pingidentity.com/pingoneaic/idm-guide/managed-objects.html) +[LDAP connector configuration](https://docs.pingidentity.com/pingoneaic/idm-connector-reference/ldap-connector.html) +[Provisioning and reconciliation](https://docs.pingidentity.com/pingoneaic/idm-guide/provisioning-overview.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md new file mode 100644 index 0000000..b45f46f --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md @@ -0,0 +1,160 @@ +--- +title: "PingOne ST — Foundation Overview" +product_family: pingone-st +products: ["pingone-aic", "pingam", "pingidm", "pingds"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "architect"] +use_cases: ["workforce", "customer"] +doc_type: concept +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/index.html" +--- + +# PingOne ST — Foundation Overview + +Conceptual orientation for PingOne ST (Advanced Identity Cloud): what it is, how it is structured, and what each component does before any configuration begins. + +## Scope + +**Covers:** Tenant architecture, core components, realm model, admin control plane. +**Does NOT cover:** Step-by-step setup tasks — see: +- `references/curated/pingone-st/app-setup.md` for application registration +- `references/curated/pingone-st/authentication-fundamentals.md` for journeys +- `references/curated/pingone-st/directory-setup.md` for identity stores and users +- `references/curated/pingone-st/themes-and-customization.md` for branding + +## Key steps / content + +### What PingOne ST is + +PingOne ST (formerly ForgeRock Identity Cloud) is a fully managed, single-tenant SaaS identity platform. It runs a complete identity stack — authentication, identity management, and directory services — inside a dedicated tenant owned by Ping Identity but configured by the customer. + +It is distinct from PingOne MT (multi-tenant cloud) in: +- Deployment model: single-tenant per customer, not shared infrastructure +- Control plane: AIC admin console at a customer-specific URL, not the shared PingOne MT admin console +- Customization depth: full journey/tree authoring, schema extension, custom scripts +- Component model: three integrated products (PingAM, PingIDM, PingDS) vs. PingOne's service-based model + +### Core components + +| Component | Role | +|---|---| +| **PingAM** | Authentication and access management. Handles OAuth 2.0, OIDC, SAML 2.0, session management, and journey execution. | +| **PingIDM** | Identity management. Manages users, roles, groups, organizations, and provisioning to external systems. | +| **PingDS** | Directory services. Backend data store for identity data. PingDS ships as the default identity store. | + +These three components are pre-integrated in every PingOne ST tenant. They share a common data plane but expose separate admin surfaces. + +### Tenant architecture + +``` +Tenant (one per customer) +└── Realm (one or more logical identity domains) + ├── Identity Store (PingDS or external LDAP/AD) + ├── Applications (OIDC/SAML clients) + ├── Journeys / Auth Trees (authentication flows) + ├── Policies (authorization rules) + └── Themes (hosted page branding) +``` + +**Tenant:** The top-level isolated environment. All configuration is scoped to a tenant. Tenants come in types: development, staging, production, sandbox — each with different SLA and capability profiles. + +**Realm:** A logical partition inside a tenant for grouping identities, applications, and authentication configuration. The default realms are `alpha` (typically customer-facing) and `bravo` (typically internal/workforce). Additional realms can be created. + +**Identity Store:** The backend user data repository associated with a realm. PingDS is the default. External LDAP/AD can be configured as an additional or replacement store. + +### Admin control plane surfaces + +| Surface | What it controls | +|---|---| +| AIC tenant admin console | Realm management, journeys, apps, themes, users, audit | +| AM admin console | Low-level OAuth2/SAML configuration, realms, advanced auth settings | +| IDM admin console | Managed objects, schema, connectors, provisioning mappings | +| REST APIs | All above surfaces; preferred for automation | + +Most day-to-day admin work happens in the AIC tenant admin console. The AM and IDM consoles are used for advanced configuration not yet surfaced in the unified console. + +### Environment types + +| Type | Purpose | +|---|---| +| Development | Feature development and testing | +| Staging | Pre-production validation | +| Production | Live traffic; highest SLA | +| Sandbox | Isolated exploration; no production data | +| UAT | User acceptance testing | + +Production tenants support multi-region high availability. + +--- + +## Component interaction model + +The three core components share data but have distinct responsibilities: + +| Component | Who configures it | Key objects it manages | +|---|---|---| +| PingAM | AIC console + AM admin console | OAuth 2.0 clients, realms, journeys, OIDC provider settings, SAML entities, sessions | +| PingIDM | IDM admin console | Managed objects (users, roles, groups, organizations), provisioning mappings, connectors, reconciliation jobs | +| PingDS | Mostly automatic | User data storage; schema extensions done in PingIDM but stored in PingDS | + +Components communicate over internal APIs. PingAM reads user data from PingDS via the IDM REST API — not directly over LDAP. This means all user attribute mappings should be configured in IDM's managed object schema, not at the LDAP level. + +--- + +## Tenant access and URL patterns + +| Surface | URL pattern | +|---|---| +| AIC tenant admin console | `https://admin..forgerock.io/` | +| AM admin console (low-level config) | `https://.forgerock.io/am/console` | +| IDM admin console | `https://.forgerock.io/platform/` | +| End-user hosted login | `https://.forgerock.io/am/XUI/` | +| OIDC discovery (alpha realm) | `https://.forgerock.io/am/oauth2/realms/root/realms/alpha/.well-known/openid-configuration` | +| AM REST API | `https://.forgerock.io/am/json/` | +| IDM REST API | `https://.forgerock.io/openidm/` | + +Custom domains replace the `.forgerock.io` hostname in all end-user-facing URLs. Admin console URLs retain the default hostname regardless of custom domain configuration. + +--- + +## First-login setup sequence + +Complete these steps in order before registering applications or onboarding users: + +1. Verify realm selection: `alpha` for customer-facing, `bravo` for internal, or plan additional realms +2. Configure identity store: verify PingDS is operational or connect external LDAP/AD +3. Set up custom domain (production tenants) +4. Configure email provider (SMTP or Ping-managed) for notification delivery +5. Apply branding theme to realm +6. Register OAuth 2.0 / OIDC applications +7. Build or import authentication journeys +8. Test end-to-end login before directing real users + +## Prerequisites + +- PingOne ST subscription provisioned by Ping Identity +- Tenant URL and initial superadmin credentials from onboarding email +- Understanding of OAuth 2.0, OIDC, or SAML 2.0 for application integration planning + +## Common variants + +| Variant | Note | +|---|---| +| ForgeRock Identity Cloud | Previous branding; same product. Documentation and community content may use this name. | +| Multiple realms | Large deployments often use separate realms for workforce vs. customer identity domains. | +| Custom domains | Production tenants should configure a custom domain before going live. | + +## Related references + +- `references/curated/pingone-st/app-setup.md` +- `references/curated/pingone-st/authentication-fundamentals.md` +- `references/curated/pingone-st/directory-setup.md` +- `references/curated/pingone-st/themes-and-customization.md` + +## Source + +[PingOne ST Documentation](https://docs.pingidentity.com/pingoneaic/index.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md new file mode 100644 index 0000000..ab9efe6 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md @@ -0,0 +1,156 @@ +--- +title: "PingOne ST — Themes and Customization" +product_family: pingone-st +products: ["pingone-aic"] +capabilities: ["foundation"] +services: [] +audience: ["admin", "developer"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/ui-customization-guide/ui-theming.html" +--- + +# PingOne ST — Themes and Customization + +Apply branding to PingOne ST hosted pages using the Theme Editor and custom CSS. + +## Scope + +**Covers:** Theme creation, visual configuration, custom CSS, theme assignment to realms and journeys, hosted page coverage. +**Does NOT cover:** Email template customization — configured separately under Notifications. Custom journey UI nodes — that is a `ping-orchestration` scripting task. + +--- + +## Theming model + +Themes are applied in layers: + +``` +Tenant +└── Realm (realm-level default theme) + └── Journey (journey-level override) + └── Hosted pages served to end users +``` + +A single tenant can serve distinct branding to different user populations by assigning different themes to different realms or journeys. + +**Admin surface:** AIC admin console → Realm → Theming → Themes → + New Theme + +--- + +## Theme configuration fields + +| Field | Notes | +|---|---| +| Logo | Appears on login, enrollment, and account pages | +| Favicon | Browser tab icon | +| Colors | Primary, secondary, link, background — hex values | +| Font | Web-safe fonts or custom font family; CDN fonts require CSP update | +| Layout | Card layout (centered, default) or full-page | + +--- + +## Custom CSS + +**Access:** Theme Editor → Custom CSS tab + +Custom CSS class names are subject to change with product updates; validate against the current rendered output before deploying overrides. + +**CSP constraint:** PingOne ST hosted pages enforce a Content Security Policy. External fonts or assets loaded from custom CSS require the origin to be added to the CSP configuration. + +**CSP admin surface:** AIC admin console → Security → Content Security Policy + +**Social button positioning:** social login buttons appear above username/password fields by default. CSS controls visual stacking only — the actual flow order is controlled by Social Provider Handler node placement in the journey. + +--- + +## Theme assignment + +| Level | Effect | Admin surface | +|---|---|---| +| Realm default | All journeys in the realm use this theme unless overridden | Realm → Theming → Set Default Theme | +| Journey override | All pages shown during that journey use this theme | Journey editor → (journey settings) → Theme | + +--- + +## Hosted pages covered by themes + +| Page type | Themed | +|---|---| +| Login / sign-on | Yes | +| Registration | Yes | +| Password reset | Yes | +| MFA enrollment | Yes | +| Account (end-user self-service) | Yes | +| Consent | Yes | +| Error pages | Partial — system errors may not be fully themed | + +--- + +## Localization + +- Hosted page strings are driven by the browser's `Accept-Language` header +- Custom locale files can be added for supported languages +- Admin console and hosted pages support localization independently + +--- + +--- + +## Email notification templates + +Email templates are separate from visual themes but affect the end-user branding experience. + +| Template category | Admin surface | +|---|---| +| Registration / welcome | Realm → Notifications → Email → Registration | +| Password reset | Realm → Notifications → Email → Password Reset | +| MFA enrollment | Realm → Notifications → Email → MFA Enrollment | +| Custom journey notifications | Realm → Notifications → Email → + New Template | + +Template editor supports HTML with Freemarker expressions for dynamic values (e.g., `${user.givenName}`, `${resetLink}`). Test templates using the preview function before saving. + +CSP note: images embedded in email templates must be hosted on an externally accessible URL — PingOne ST does not host email images; reference them via absolute URL in the template HTML. + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| Theme change not visible to end users | Hosted page still shows old branding | Theme must be set as default in the realm, or assigned explicitly to the journey or app | +| Custom font blocked by CSP | Font request fails; fallback font renders instead | Add the font CDN origin to Security → Content Security Policy → `font-src` directive | +| Social button CSS override does not reorder buttons | Social buttons appear in unexpected position despite CSS | Social button position is controlled by the Social Provider Handler node position in the journey; CSS controls visual style only | +| Account pages not themed | End-user account page shows default Ping branding | Account page theming is managed under Realm → Theming → Account Theme — different from the main Login theme | +| Dark mode not applying | Custom CSS `prefers-color-scheme` media query ignored | CSP or class name changes between product versions may break CSS selectors; re-inspect rendered DOM after each product update | +| Error pages partially themed | System error pages (500-level) still show Ping default theme | PingAM system error pages are not fully themeable; customize error messages via AM admin console → Realm → General Settings → Error Page | + +## Prerequisites + +- PingOne ST tenant with at least one realm +- Admin access to Theming +- If using CDN fonts: CSP must allow the font origin before the theme is applied + +## Common variants + +| Variant | Note | +|---|---| +| Multi-brand | One theme per brand; assign each to the appropriate realm or journey | +| Workforce vs. CIAM | Use realm-level themes to separate internal and external page branding | +| Dark mode | Not natively supported; achievable via custom CSS overriding color variables | +| Account pages | End-user account management at `/am/XUI/` is also themed but configured separately under Account pages | + +## Related references + +- `references/curated/pingone-st/foundation-overview.md` +- `references/curated/pingone-st/authentication-fundamentals.md` +- `references/curated/pingone-st/app-setup.md` + +## Source + +[UI theming — PingOne ST](https://docs.pingidentity.com/pingoneaic/ui-customization-guide/ui-theming.html) +[UI customization overview](https://docs.pingidentity.com/pingoneaic/ui-customization-guide/ui-overview.html) +[Getting started: apply basic branding](https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-apply_branding.html) diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/cross-platform/top-10.json b/plugins/ping-identity/skills/ping-foundation/references/generated/cross-platform/top-10.json new file mode 100644 index 0000000..a2c5f78 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/cross-platform/top-10.json @@ -0,0 +1,61 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-foundation", + "branch": "cross-platform", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 10, + "docs": [ + { + "title": "Core Admin Patterns", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/core-admin-patterns.md", + "url": "https://docs.pingidentity.com/pingone/integrations/p1_ldap_gateways.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "Policy and Branding Basics", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md", + "url": "https://docs.pingidentity.com/pingone/authentication/p1_authenticationpolicies.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "Tenant and Environment Setup", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/tenant-and-environment-setup.md", + "url": "https://docs.pingidentity.com/pingone/environments/p1_c_environments.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "Ping Foundation Overview", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/foundation-overview.md", + "url": "https://docs.pingidentity.com/pingone/introduction_to_pingone/p1_introduction.html", + "doc_type": "concept", + "capabilities": [ + "foundation" + ], + "score": 0.9, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingaccess.md b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingaccess.md new file mode 100644 index 0000000..558fe23 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingaccess.md @@ -0,0 +1,30 @@ +--- +title: "Ping Software Suite — PingAccess" +product_family: ping-software +products: ["pingaccess"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# Ping Software Suite — PingAccess + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Installation and initial setup +- Virtual host and site configuration +- Protected resource (application) setup +- Agent and engine configuration +- Token provider integration (PingFederate) +- SSL/TLS certificate management +- Clustering and high availability + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `ping-software` → `foundation` + `pingaccess` product entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingam.md b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingam.md new file mode 100644 index 0000000..d57d04c --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingam.md @@ -0,0 +1,30 @@ +--- +title: "PingAM — Foundation" +product_family: ping-software +products: ["pingam"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingAM — Foundation + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- PingAM standalone installation and initial configuration +- Realm and identity store setup +- Authentication tree and journey basics (see ping-orchestration for design patterns) +- OAuth 2.0 / OIDC authorization server +- Federation (SAML IdP and SP) +- Certificate and keystore management +- Clustering + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `ping-software` → `foundation` + `pingam` product entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingdirectory.md b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingdirectory.md new file mode 100644 index 0000000..6225a6b --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingdirectory.md @@ -0,0 +1,31 @@ +--- +title: "Ping Software Suite — PingDirectory" +product_family: ping-software +products: ["pingdirectory"] +capabilities: ["foundation"] +audience: ["admin", "operator"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# Ping Software Suite — PingDirectory + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Installation and initial setup +- Schema configuration and extension +- Replication topology setup +- Access control (ACIs) +- Backends and backend databases +- Password storage and policies +- SCIM and REST API configuration +- PingDataSync integration + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `ping-software` → `foundation` + `pingdirectory` product entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingfederate.md b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingfederate.md new file mode 100644 index 0000000..31c6ad6 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingfederate.md @@ -0,0 +1,31 @@ +--- +title: "PingFederate — Foundation" +product_family: ping-software +products: ["pingfederate"] +capabilities: ["foundation"] +audience: ["admin", "architect"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingFederate — Foundation + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Installation and initial configuration +- Server settings (base URL, certificates, runtime node) +- IdP and SP connection setup (SAML, OIDC) +- OAuth 2.0 authorization server configuration +- Authentication adapters and policies +- Password Credential Validators +- Data stores (LDAP, JDBC) +- Clustering and high availability + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `ping-software` → `foundation` + `pingfederate` product entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingid.md b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingid.md new file mode 100644 index 0000000..bcfecfa --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/pingid.md @@ -0,0 +1,29 @@ +--- +title: "Ping Software Suite — PingID" +product_family: ping-software +products: ["pingid"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# Ping Software Suite — PingID + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- PingID on-premises setup and configuration +- PingID integration with PingFederate +- MFA policy configuration +- Device enrollment and management +- Notification service configuration (push, SMS, email) +- PingID SDK integration points + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `ping-software` → `foundation` + `pingid` product entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/top-25.json b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/top-25.json new file mode 100644 index 0000000..2333714 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/ping-software/top-25.json @@ -0,0 +1,48 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-foundation", + "branch": "ping-software", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 25, + "docs": [ + { + "title": "PingAccess \u2014 Administration Basics", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingaccess-basics.md", + "url": "https://docs.pingidentity.com/pingaccess/9.0/pa_landing_page.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingFederate \u2014 Administration Basics", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md", + "url": "https://docs.pingidentity.com/pingfederate/latest/administrators_reference_guide/pf-admin-guide.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingDirectory \u2014 Administration Basics", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingdirectory-basics.md", + "url": "https://docs.pingidentity.com/pingdirectory/latest/pd-directory-server-administration-guide/pd-ds-admin-overview.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/apps.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/apps.md new file mode 100644 index 0000000..92ae178 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/apps.md @@ -0,0 +1,28 @@ +--- +title: "PingOne MT — Applications" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin", "developer"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne MT — Applications + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Adding OIDC and SAML applications +- Native/mobile app registration +- Worker app registration (machine-to-machine) +- App attribute mapping +- Application policies and assignments + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-mt` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/directories.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/directories.md new file mode 100644 index 0000000..88dd372 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/directories.md @@ -0,0 +1,28 @@ +--- +title: "PingOne MT — Directories" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne MT — Directories + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- PingOne Directory (built-in) +- External LDAP/AD connection +- User populations and attribute schema +- User import and provisioning +- SCIM provisioning from HR systems + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-mt` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/policies.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/policies.md new file mode 100644 index 0000000..7842a28 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/policies.md @@ -0,0 +1,28 @@ +--- +title: "PingOne MT — Policies" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne MT — Policies + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Sign-on policies and actions +- MFA policies and device management +- Password policies +- Risk-based policies (requires PingOne Protect) +- Agreement and progressive profiling actions + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-mt` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/tenants.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/tenants.md new file mode 100644 index 0000000..c4b8a9d --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/tenants.md @@ -0,0 +1,28 @@ +--- +title: "PingOne MT — Tenants and Environments" +product_family: pingone-mt +products: ["pingone"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne MT — Tenants and Environments + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Environment creation and management +- Organization and license management +- Region selection and data residency +- Environment-level settings (custom domains, notifications, branding) +- Admin roles and delegated administration + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-mt` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/top-25.json b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/top-25.json new file mode 100644 index 0000000..3e09d95 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-mt/top-25.json @@ -0,0 +1,88 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-foundation", + "branch": "pingone-mt", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 25, + "docs": [ + { + "title": "PingOne MT \u2014 Sign-On Policies and MFA Configuration", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/sign-on-policies.md", + "url": "https://docs.pingidentity.com/pingone/authentication/p1_authenticationpolicies.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne MT \u2014 Application Registration", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/app-registration.md", + "url": "https://docs.pingidentity.com/pingone/applications/p1_applications_add_applications.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne MT \u2014 Themes, Branding, and Notifications", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/themes-and-branding.md", + "url": "https://docs.pingidentity.com/pingone/branding/p1_branding.html", + "doc_type": "guide", + "capabilities": [ + "foundation", + "branding" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "PingOne MT \u2014 Tenant and Environment Setup", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/tenant-and-environment-setup.md", + "url": "https://docs.pingidentity.com/pingone/platformconsole/p1_c_environments.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne MT \u2014 Administrator Roles and Access Management", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/admin-roles-and-access.md", + "url": "https://docs.pingidentity.com/pingone/administrators/p1_admin_roles.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne MT Directory Options and Population Management", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-mt/directory-and-populations.md", + "url": "https://docs.pingidentity.com/pingone/directory/p1_aboutusers.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/administration.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/administration.md new file mode 100644 index 0000000..b776c91 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/administration.md @@ -0,0 +1,28 @@ +--- +title: "PingOne ST — Administration" +product_family: pingone-st +products: ["pingone-st", "pingam", "pingidm", "pingds"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne ST — Administration + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Tenant and realm management +- Admin roles and delegated administration +- Custom domains +- Email and notification configuration +- Audit logging and monitoring + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-st` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/customization.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/customization.md new file mode 100644 index 0000000..e3cb10d --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/customization.md @@ -0,0 +1,27 @@ +--- +title: "PingOne ST — Customization" +product_family: pingone-st +products: ["pingone-st"] +capabilities: ["foundation"] +audience: ["admin", "developer"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne ST — Customization + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Theming and hosted page customization +- Custom CSS and logo +- Localization and language support +- Custom domains and certificates + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-st` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/identity-data.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/identity-data.md new file mode 100644 index 0000000..3ddb4cd --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/identity-data.md @@ -0,0 +1,28 @@ +--- +title: "PingOne ST — Identity Data" +product_family: pingone-st +products: ["pingone-st", "pingidm", "pingds"] +capabilities: ["foundation"] +audience: ["admin", "developer"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne ST — Identity Data + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Identity schema and attribute configuration (PingIDM) +- PingDS directory setup and schema extension +- User import, provisioning, and reconciliation +- LDAP/AD integration +- SCIM provisioning + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-st` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/reports.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/reports.md new file mode 100644 index 0000000..956da09 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/reports.md @@ -0,0 +1,27 @@ +--- +title: "PingOne ST — Reports" +product_family: pingone-st +products: ["pingone-st"] +capabilities: ["foundation"] +audience: ["admin"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne ST — Reports + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- Audit log access and filtering +- Authentication event reports +- Admin event logs +- Log forwarding (Splunk, Elastic, SIEM) + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-st` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/self-service.md b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/self-service.md new file mode 100644 index 0000000..c6de28d --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/self-service.md @@ -0,0 +1,28 @@ +--- +title: "PingOne ST — Self-Service" +product_family: pingone-st +products: ["pingone-st"] +capabilities: ["foundation"] +audience: ["admin", "developer"] +doc_type: reference +status: current +canonical: false +last_updated: "" +slug: "" +--- + +# PingOne ST — Self-Service + +_Generated reference stub. Replace with curated content or populate from docs manifest._ + +## Topics covered by this branch + +- End-user self-service portal configuration +- Password reset and account recovery +- Profile update flows +- Progressive profiling +- Consent management + +## Source + +Populate from: `shared/generated/docs-by-platform.json` → `pingone-st` → `foundation` capability entries. diff --git a/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/top-25.json b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/top-25.json new file mode 100644 index 0000000..2bab11f --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/generated/pingone-st/top-25.json @@ -0,0 +1,74 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-foundation", + "branch": "pingone-st", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 25, + "docs": [ + { + "title": "PingOne ST \u2014 Authentication Fundamentals", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/authentication-fundamentals.md", + "url": "https://docs.pingidentity.com/pingoneaic/am-journey-guide/journey-overview.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne ST \u2014 Directory Setup (User Management)", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/directory-setup.md", + "url": "https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-identity_store.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne ST \u2014 App Setup", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/app-setup.md", + "url": "https://docs.pingidentity.com/pingoneaic/getting_started/getting_started-create_oauth2_client.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne ST \u2014 Themes and Customization", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/themes-and-customization.md", + "url": "https://docs.pingidentity.com/pingoneaic/ui-customization-guide/ui-theming.html", + "doc_type": "guide", + "capabilities": [ + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + }, + { + "title": "PingOne ST \u2014 Foundation Overview", + "slug": "plugins/ping-identity/skills/ping-foundation/references/curated/pingone-st/foundation-overview.md", + "url": "https://docs.pingidentity.com/pingoneaic/index.html", + "doc_type": "concept", + "capabilities": [ + "foundation" + ], + "score": 0.9, + "canonical": true, + "last_updated": "2026-06-02", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-foundation/references/runtime/docs-mcp-routing.md b/plugins/ping-identity/skills/ping-foundation/references/runtime/docs-mcp-routing.md new file mode 100644 index 0000000..93f6275 --- /dev/null +++ b/plugins/ping-identity/skills/ping-foundation/references/runtime/docs-mcp-routing.md @@ -0,0 +1,38 @@ +--- +title: Docs MCP routing — ping-foundation +status: current +last_updated: 2026-05-29 +--- + +# Runtime tier — Docs MCP routing for ping-foundation + +This file describes when and how this skill falls back to live Docs MCP retrieval. It is the third tier in the strategy doc § 0 "Agent Path". + +## When to escalate to Docs MCP + +Use Docs MCP only when: +1. The 1–3 curated anchors loaded from `references/curated/` did not answer the question. +2. The bounded shortlist in `references/generated//top-N.json` did not fill the gap. +3. The user's task requires version-specific, current, or long-tail information (e.g., a recently released feature, a deprecation note, a specific API field). + +If any of these is false, do NOT call Docs MCP. Strategy doc § 0 mandates "use the smallest trusted context first." + +## Surgical query rules + +When Docs MCP is required, query it with: +- The exact platform family (PingOne MT, PingOne ST, Ping Software Suite) +- The exact product or service name +- The exact capability (e.g., "MFA policy", not "authentication") +- A version constraint when applicable + +Retrieve specific sections, not full page dumps. + +## Helix as a runtime path + +Production-bound execution runs through Helix conversation APIs. The decision rule for sandbox-vs-production lives in `rules/runtime-selection.md`. Helix is **not** a v1 skill; it is a runtime tier referenced from this file. + +## Related + +- `rules/runtime-selection.md` +- `references/curated/` — tier 1 +- `references/generated/` — tier 2 diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/SKILL.md b/plugins/ping-identity/skills/ping-identity-for-ai/SKILL.md new file mode 100644 index 0000000..dc51f99 --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/SKILL.md @@ -0,0 +1,82 @@ +--- +name: ping-identity-for-ai +description: AI-era identity patterns for the Ping Identity platform. Use this skill whenever a task explicitly involves an AI agent, LLM, or agentic workflow — giving an AI agent a verified identity, securing agent-to-API access with client credentials, applying Verified Trust signals, delegated tokens for helpdesk AI bots, or Ping Identity's Identity for AI solution. Do NOT use for generic automated processes or batch jobs without explicit AI/LLM context — if the request says "automated process" or "scheduled job" without mentioning AI or an agent, ask a clarifying question first. Also invoke with /ping-identity-for-ai. +compatibility: Designed for AI identity and agent security work on Ping Identity platforms. References product docs and Ping Labs content. +metadata: + publisher: Ping Identity + version: "0.2.0" +--- + +# ping-identity-for-ai + +AI-era identity patterns: Identity for AI, Verified Trust, agent identity, agent security, and AI application authentication. + +## Invocation + +Invoke explicitly with `/ping-identity-for-ai` or by saying "use ping-identity-for-ai to...". + +## When to use this skill + +- "Give my AI agent a verified identity for API access" +- "Use Verified Trust signals in my MCP server" +- "Secure agent-to-agent authentication using Ping" +- "Workforce helpdesk AI — how do I authenticate users for my AI assistant?" +- "Identity for AI architecture with Ping" +- "AI app authentication patterns on PingOne" +- "Agent security and authorization for agentic workflows" +- "Short-lived tokens or token rotation for an autonomous agent" +- "Delegated token for an AI assistant acting on behalf of an employee" +- "Revoke a compromised AI agent's access immediately" + +## When NOT to use this skill + +- General platform setup or tenant administration: use `ping-foundation`. +- Building a journey or DaVinci flow for human authentication (not AI-specific): use `ping-orchestration`. +- App SDK integration without AI context: use `ping-app-integration`. +- User is orienting or selecting a platform: use `ping-quickstart`. +- Using Ping Protect, PingOne Verify, or PingOne Authorize as standalone services without AI agent context: use `ping-universal-services`. +- "Automated process", "batch job", "scheduled task", or "service account" **without any AI / LLM / agent context** — ask a clarifying question to determine whether this involves an AI agent or a conventional machine process before routing here. + +## Multi-skill use cases + +A complete AI identity solution typically spans multiple skills: + +| Layer | Skill | +|---|---| +| Platform setup and app registration | `ping-foundation` | +| Auth flow / journey / DaVinci design | `ping-orchestration` | +| AI identity patterns and Verified Trust | `ping-identity-for-ai` (this skill) | +| App / SDK integration | `ping-app-integration` | + +**Example sequence:** + +1. `ping-foundation` — register the AI agent application and configure the environment. +2. `ping-identity-for-ai` — design the token scoping and delegation model (this skill). +3. `ping-orchestration` — build the step-up MFA flow using DaVinci or Journey. +4. `ping-app-integration` — integrate the OIDC client in the AI application. + +Complete agent token scoping, Verified Trust signal design, and delegation model here, then hand off to `ping-orchestration` for step-up MFA flows and `ping-app-integration` for SDK/OIDC client wiring. + +## Routing — Step 1: What are you trying to do? + +| Task | Curated anchor | +|---|---| +| Overview / strategy for Identity for AI (5-pillar) | `references/curated/identity-for-ai-overview.md` | +| Register an AI agent as a managed identity | `references/curated/agent-security-patterns.md` | +| Machine-to-machine auth, token scoping, rotation, revocation | `references/curated/agent-security-patterns.md` | +| CIBA human-in-the-loop approvals for high-risk agent actions | `references/curated/agent-security-patterns.md` | +| Bot / agentic AI detection in flows (Protect) | `references/curated/agent-security-patterns.md` | +| Protect / secure an MCP server (PingGateway) | `references/curated/agent-gateway-mcp.md` | +| Apply Verified Trust signals or verifiable credentials | `references/curated/verified-trust-overview.md` | +| Workforce helpdesk AI — delegation + step-up pattern | `references/curated/workforce-helpdesk-ai.md` | +| AI application end-user authentication (delegated) | `references/curated/workforce-helpdesk-ai.md` | + +## Step 2: Load curated anchors (1–3 max) + +Load the anchors identified in Step 1. Stop if the curated anchor is sufficient. Do not load all four anchors unless the user's task explicitly spans all sub-areas. + +## Retrieval escalation + +1. Curated anchors (`references/curated/`) — load 1–3 max. Stop if sufficient. +2. Generated shortlist (`references/generated/`) — load if curated did not fully answer the task. +3. Docs MCP fallback — `references/runtime/docs-mcp-routing.md`. Only if curated + shortlist insufficient. diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/ping-marketplace.json b/plugins/ping-identity/skills/ping-identity-for-ai/ping-marketplace.json new file mode 100644 index 0000000..53a2f8b --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/ping-marketplace.json @@ -0,0 +1,32 @@ +{ + "skill_id": "ping-identity-for-ai", + "display_name": "Ping Identity — Identity for AI", + "description": "AI-era identity patterns: Identity for AI, Verified Trust, agent identity, agent security, AI app authentication, and workforce helpdesk AI use cases.", + "version": "0.2.0", + "publisher": "Ping Identity", + "tags": { + "product_family": ["pingone-mt", "pingone-st", "cross-platform"], + "products": [ + "pingone", + "pingone-st", + "verified-trust" + ], + "capabilities": ["identity-for-ai"], + "audience": ["developer", "architect"], + "use_cases": ["workforce", "ai-identity"] + }, + "entry_point": "SKILL.md", + "references": { + "curated_path": "references/curated/", + "generated_path": "references/generated/" + }, + "related_skills": [ + "ping-quickstart", + "ping-foundation", + "ping-orchestration", + "ping-app-integration" + ], + "min_context_tokens": 500, + "max_curated_docs": 3, + "max_shortlist_docs": 20 +} diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/.gitkeep b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-gateway-mcp.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-gateway-mcp.md new file mode 100644 index 0000000..e09592f --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-gateway-mcp.md @@ -0,0 +1,186 @@ +--- +title: "Agent Gateway — Securing MCP Servers with PingGateway" +product_family: cross-platform +products: ["pinggateway", "pingone-aic", "pingone", "pingfederate"] +capabilities: ["identity-for-ai"] +services: [] +audience: ["developer", "architect"] +use_cases: ["ai-identity"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pinggateway/2026/mcp/index.html" +--- + +# Agent Gateway — Securing MCP Servers with PingGateway + +How to use PingGateway's Agent Gateway module as an MCP security gateway — protecting Model Context Protocol servers from unauthorized AI agent access using OAuth 2.0 resource server semantics. + +## Scope + +**Covers:** What the Agent Gateway module does, the three MCP filters (McpAuditFilter, McpProtectionFilter, McpValidationFilter), OAuth AS choices, RFC 8707 resource indicator requirement, version requirements, and architecture placement. + +**Does NOT cover:** OAuth 2.0 AS configuration (PingOne, AIC, PingFederate) — see `ping-foundation`. Agent token scoping and client credentials patterns — see `references/curated/agent-security-patterns.md`. DaVinci flow design — see `ping-orchestration`. + +> **Stability note:** The Agent Gateway module has **Evolving interface stability** — it is subject to change without notice even in minor or maintenance releases. Plan for configuration updates when upgrading PingGateway. + +--- + +## What the Agent Gateway module does + +MCP (Model Context Protocol) is an open standard for connecting AI agents to servers that expose tools and data. Without a security layer, MCP servers must implement their own OAuth 2.0 validation, audit logging, and throttling — inconsistently, per server. + +PingGateway's Agent Gateway module sits between the MCP client (AI agent) and the MCP server, acting as a centralized security proxy. A single PingGateway route protects every MCP server behind it without requiring per-server security code. + +``` +AI Agent (MCP client) + │ + ▼ +PingGateway (Agent Gateway module) + ├─ McpValidationFilter — validates MCP message format + protocol version + ├─ McpProtectionFilter — enforces OAuth 2.0 Bearer token, binds resource scopes (RFC 8707) + └─ McpAuditFilter — records structured audit trail of all agent requests + │ + ▼ +MCP Server (backend tools / data) +``` + +PingGateway acts as an **OAuth 2.0 resource server** (RS). It validates access tokens issued by the configured AS, enforces coarse-grained access control, and passes authorized requests to the MCP server. + +--- + +## The three MCP filters + +### McpProtectionFilter + +Enforces OAuth 2.0 security on inbound MCP requests. + +| Responsibility | Detail | +|---|---| +| Bearer token validation | Validates the agent's access token against the configured OAuth AS (introspection or local JWT verification) | +| Resource indicator binding (RFC 8707) | Binds the scope check to the specific MCP resource server; prevents token reuse across services | +| Coarse-grained access control | Enforces scope requirements; grants or denies access based on token claims | +| Token introspection caching | Caches introspection results to reduce per-request AS calls | + +**RFC 8707 requirement:** The OAuth AS must support Resource Indicators (RFC 8707). For AIC, an OAuth 2.0 Access Token Modification script must be configured to include the resource indicator in the token. Tokens without the correct resource indicator are rejected. + +### McpValidationFilter + +Validates the structure and version of inbound MCP messages. + +| Check | Detail | +|---|---| +| MCP client message format | Validates message structure, excluding tool schemas | +| Protocol version rewrite | Rewrites the `initialize` request to PingGateway's supported MCP protocol version (`2025-06-18`) | +| SSE support | If the MCP server uses server-sent events (SSE), enable streaming in PingGateway | + +### McpAuditFilter + +Records centralized audit trail entries for all MCP agent requests. + +| Audit field | Source | +|---|---| +| Agent identity | Extracted from the validated access token (`sub` or `client_id`) | +| MCP action | Tool called, resource accessed | +| Timestamp | PingGateway request time (UTC) | +| Outcome | Success / failure / authorization denial | + +Configure `McpAuditFilter` with an `AuditService` reference defined in the PingGateway heap. Both `AuditService` inline and named references are supported. + +--- + +## OAuth AS choices + +PingGateway works with any OAuth 2.0 AS. For MCP protection, the three Ping-native options are: + +| AS | Use when | +|---|---| +| **PingOne Advanced Identity Cloud (AIC)** | AIC-managed workloads; agent registered as AIC AI Agent client (`/aiagent/register`); Journey-based step-up available | +| **PingOne MT** | Cloud-first; PingOne OAuth AS; agent registered as a Worker application | +| **PingFederate** | On-premises or hybrid; PingFederate AS with RFC 8707 resource indicator support; supports `private_key_jwt` and mTLS | + +All three require RFC 8707 support on the AS side. Verify before deployment. + +--- + +## Architecture placement + +``` +[AI Agent] + └─ MCP request + Bearer token + ↓ +[PingGateway — Agent Gateway module] + ├─ Introspect / validate token → [OAuth AS: AIC / PingOne / PingFederate] + ├─ Check resource indicator (RFC 8707) scope + ├─ Audit log entry + └─ Forward if valid + ↓ +[MCP Server — backend tools/data] +``` + +**PingGateway as reverse proxy:** PingGateway intercepts all traffic to the MCP server. The MCP server requires no OAuth logic; it trusts requests that pass through PingGateway. + +**Multiple MCP servers:** Deploy one PingGateway route per MCP server endpoint. Each route uses the same filter configuration pattern with server-specific resource indicators. + +--- + +## Version requirements + +| PingGateway version | MCP support status | +|---|---| +| 2026.3.0 (2026.x train — current) | MCP gateway + Agent Gateway module GA | +| 2025.11.2 (2025.x train — latest maintenance) | MCP gateway available (introduced 2025.11.1) | +| Earlier than 2025.11.1 | No MCP support | + +Use the 2026.x train for new deployments. The 2025.11.x train remains supported under the 2025 maintenance schedule. + +The Agent Gateway module is independent of other PingGateway modules — no other modules are prerequisites. + +--- + +## Common failure modes + +| Symptom | Likely cause | Fix | +|---|---|---| +| 401 on all MCP requests | Bearer token missing or expired | Verify agent requests include `Authorization: Bearer `; check AS token TTL | +| 403 after token validation | Resource indicator scope mismatch | Confirm RFC 8707 resource indicator in token matches the MCP server's registered resource; check AS script | +| Protocol version error from MCP server | MCP protocol version mismatch | McpValidationFilter rewrites to `2025-06-18`; verify MCP server supports this version | +| Audit records missing | McpAuditFilter not in route, or AuditService misconfigured | Verify filter order and AuditService heap reference | +| SSE connections dropping | Streaming not enabled in PingGateway | Enable streaming in the PingGateway route for SSE-based MCP servers | + +--- + +## Prerequisites + +- PingGateway 2025.11.1+ or 2026.x installed and network-accessible from AI agent clients +- OAuth AS (AIC, PingOne, or PingFederate) with RFC 8707 Resource Indicators support enabled +- MCP server accessible from PingGateway +- Agent client registration completed (see `references/curated/agent-security-patterns.md`) +- For AIC: OAuth 2.0 Access Token Modification script configured to support RFC 8707 + +## Common variants + +| Variant | Notes | +|---|---| +| Multiple MCP servers | One route per server; same filter pattern; different resource indicators per server | +| PingOne Authorize integration | Add PingOne Authorize call inside the route for fine-grained ABAC policy on top of OAuth scope control | +| PingOne Protect integration | Add Protect evaluation in the route to detect agentic bot activity and block high-risk agent requests | +| Cloudflare Workers MCP server | PingGateway route proxies to Cloudflare Workers endpoint; same token validation applies | +| AWS Bedrock agents | Bedrock agent obtains token via client credentials; PingGateway validates before forwarding to MCP tools | + +## Related references + +- `references/curated/agent-security-patterns.md` — token scoping, client credentials, revocation +- `references/curated/identity-for-ai-overview.md` — 5-pillar ID4AI architecture +- `references/curated/workforce-helpdesk-ai.md` — delegation and HITL patterns + +## Source + +- https://docs.pingidentity.com/pinggateway/2026/mcp/index.html +- https://docs.pingidentity.com/pinggateway/2026/reference/McpAuditFilter.html +- https://docs.pingidentity.com/pinggateway/2026/reference/McpProtectionFilter.html +- https://docs.pingidentity.com/pinggateway/2026/reference/McpValidationFilter.html +- https://docs.pingidentity.com/platform/8/platform-guide/edge-security.html +- https://developer.pingidentity.com/identity-for-ai/release-notes/idai-whats-new.html +- https://docs.pingidentity.com/pinggateway/release-notes/whats-new.html diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-security-patterns.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-security-patterns.md new file mode 100644 index 0000000..7f12bef --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-security-patterns.md @@ -0,0 +1,275 @@ +--- +title: "Agent Security Patterns — Securing AI Agents with Ping Identity" +product_family: cross-platform +products: ["pingone", "pingfederate"] +capabilities: ["identity-for-ai", "foundation"] +services: [] +audience: ["developer", "architect"] +use_cases: ["ai-identity"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/ai_agents/p1_ai_agents.html" +--- + +# Agent Security Patterns — Securing AI Agents with Ping Identity + +Patterns for securing autonomous and semi-autonomous AI agents that call Ping Identity OAuth-protected APIs or downstream resources, covering machine-to-machine auth, token scoping, rotation, revocation, and audit. + +## Scope + +Covers: OAuth 2.0 client credentials flow as the default machine-to-machine pattern for AI agents, token scoping strategy, short-lived token rotation, revocation on compromise, and correlatable audit patterns. +Does NOT cover: human-in-the-loop delegation (see `references/curated/workforce-helpdesk-ai.md`), Verified Trust signal issuance (see `references/curated/verified-trust-overview.md`), or standard OIDC application registration for user-facing apps (see `ping-foundation`). + +--- + +## Pattern 0: Register the agent as a managed identity + +Before any token is issued, the AI agent should be registered as a first-class identity — not just an anonymous OAuth2 client. This enables lifecycle management, ownership tracking, and audit. + +### PingOne — AI Agents feature + +PingOne supports registering AI agents as managed identities via the **AI Agents** admin surface (requires Agent IAM Core license; contact Ping Identity Sales). + +| Registration field | Notes | +|---|---| +| Agent name | Human-readable label; appears in audit logs and admin console | +| Owner | The human or team responsible for the agent | +| Client ID | Auto-generated OAuth 2.0 client ID | +| Client authentication method | `private_key_jwt` (recommended) or `client_secret_basic` | +| Scopes | Minimum required; configured at registration | + +Registered agents appear in the AI Agents list (Directory > AI Agents in the PingOne console) alongside human identities. + +### PingOne AIC — Dynamic agent onboarding via `/aiagent/register` + +AIC provides a dedicated DCR endpoint for AI agents: `/aiagent/register` (in addition to the standard `/register`). This endpoint onboards AI agents as dynamic OAuth 2.0 clients with agent-specific defaults. + +> **Availability:** Production-ready as of 2026-06-03. Currently available on the **Rapid channel** only; Regular channel promotion is planned. + +AIC AI agents can perform tasks on behalf of end users through a delegated token exchange process (RFC 8693), maintaining distinct accountability and granular access control. + +### Agent detection — know your agents + +- Track all agent registrations in a registry (admin console, IDP admin API, or CMDB). +- Each agent should have a documented owner and expiry/renewal date. +- Disable agents that are decommissioned — do not leave them in a dormant but active state. + +--- + +## Pattern 1: Machine-to-machine auth with client credentials + +The OAuth 2.0 client credentials grant is the default pattern for AI agents that operate without a human in the loop. The agent authenticates directly to the authorization server using its own client identity — no user is involved. + +### How it works + +``` +AI Agent ──► POST /token (client_id, client_secret or private_key_jwt) + │ + ▼ + Ping OAuth AS (PingOne / PingFederate) + │ + ▼ + Access Token (JWT, short TTL) + │ +AI Agent ──► API call with Bearer token + │ + ▼ + Resource Server (validates token) +``` + +### Client authentication methods — comparison + +| Method | When to use | Credential type | Key constraint | +|---|---|---|---| +| `client_secret_post` | Dev / low-assurance only | Shared secret in request body | Secret must be rotated on any exposure; never use in production multi-agent deployments | +| `client_secret_basic` | Dev / low-assurance only | HTTP Basic auth header | Same constraints as `client_secret_post` | +| `private_key_jwt` | Production agents | Asymmetric key pair; agent holds private key | Preferred for production; key material never leaves the agent; supports key rotation without AS coordination | +| mTLS (`tls_client_auth`) | High-assurance or regulated environments | X.509 certificate | Requires mTLS-capable infrastructure; eliminates secret-in-flight; supported by PingFederate and PingOne | + +**Decision rule:** Use `private_key_jwt` or mTLS for any agent running in production. `client_secret_*` is acceptable only for local development or test environments where the secret lifetime is controlled. + +--- + +## Pattern 2: Token scoping strategy + +Each AI agent must receive the minimum set of scopes required for its specific capability. Broad scopes create blast-radius risk if an agent is compromised or misconfigured. + +### Scoping principles + +| Principle | Rationale | +|---|---| +| One agent registration per distinct capability | Prevents a compromised component from using scopes it does not need | +| No wildcard scopes for agents | Even if the AS supports wildcard grants, agents must enumerate required scopes at registration time | +| Separate read and write scopes | An agent that only reads data must not hold a write scope | +| Resource indicator scopes (RFC 8707) | Bind a scope to a specific resource server — prevents token reuse across services | + +### Scope model example + +| Agent | Required scope | Forbidden scope | +|---|---|---| +| AI helpdesk read agent | `directory:read profile:read` | `directory:write admin:*` | +| AI provisioning agent | `directory:write group:assign` | `admin:environment` | +| AI audit agent | `audit:read events:read` | Any write scope | + +--- + +## Pattern 3: Short-lived token rotation + +Agent access tokens must have a shorter TTL than tokens issued to human users, because agents operate continuously and may be compromised without immediate human detection. + +| Token type | Recommended TTL | Rationale | +|---|---|---| +| Agent access token | 5–15 minutes | Limits the window a stolen token remains valid | +| Refresh token (if used) | 1–4 hours with rotation | Each use issues a new refresh token and invalidates the old one; detect replay attacks | +| Service account token (PingFederate) | 10–30 minutes | Same principle; refresh via client credentials, not long-lived token storage | + +**Anti-pattern:** Caching an agent access token until it expires and reusing it across many requests over hours. Even if the token is technically valid, long-lived token reuse increases exposure window. + +**Implementation:** Use the `expires_in` field in the token response. Proactively refresh when `expires_in < refresh_threshold` (recommended: 20% of TTL remaining). + +--- + +## Pattern 4: Revocation + +When an AI agent is decommissioned, compromised, or misbehaving, access must be cut immediately — not at token expiry. + +### Revocation mechanisms + +| Mechanism | Latency | Scope | +|---|---|---| +| Client credential deactivation (PingOne / PingFederate) | Immediate at AS; propagates to resource servers at next token introspection | Prevents new token issuance; existing tokens valid until TTL | +| Token introspection + blacklist | ~50–100 ms per call | Resource server calls AS to validate each token; blacklist entry blocks immediately | +| Short TTL + no refresh token | Max token TTL | Simplest approach; no revocation infrastructure needed; requires short TTL discipline | +| Delete application / client registration | Immediate — all tokens orphaned | Decommissions agent entirely; use for permanent removal | + +**Decision rule:** +- For immediate revocation during an incident: disable the client registration and add the agent's `client_id` to a token introspection blacklist. +- For routine decommissioning: delete the client registration and let outstanding tokens expire. +- For compromised tokens: require introspection at every resource server call (trade latency for security). + +--- + +## Pattern 5: Audit trail + +Every API call made by an AI agent must be attributable to that agent, the action taken, and when it happened. Audit trail requirements exist independent of the auth pattern. + +### Audit fields per agent request + +| Field | Source | How to carry it | +|---|---|---| +| `agent_id` | Agent's `client_id` or `sub` claim in access token | JWT claim; resource server extracts from token | +| `request_id` | Generated by agent per request (UUID v4) | `X-Request-ID` header or `jti` (JWT ID) in a request-bound token | +| `user_id` (if delegated) | Subject claim from delegated token | JWT `sub` or `act.sub` (RFC 8693 token exchange) | +| `action` | Resource + HTTP method | Logged by resource server | +| `timestamp` | UTC ISO 8601 | `iat` claim in token + resource server log | +| `ip` or `agent_node` | Agent runtime environment | `X-Forwarded-For` or custom header | + +**Constraint:** The `request_id` must be stable across retries of the same logical request — use a correlation ID that survives retry loops, not a new UUID per HTTP attempt. + +--- + +## Auth pattern comparison table + +| Auth pattern | When to use | Ping product | Token type | Notes | +|---|---|---|---|---| +| Client credentials (`private_key_jwt`) | Production autonomous agent, no user | PingOne, PingFederate | Short-lived JWT | Default recommendation for agents | +| Client credentials (mTLS) | Regulated or high-assurance agent environments | PingFederate | Short-lived JWT | Requires certificate infrastructure | +| Token exchange (RFC 8693) | Agent acting on behalf of a user (delegated) | PingFederate | Delegated JWT | See `references/curated/workforce-helpdesk-ai.md` | +| Device authorization grant | Agent on constrained device with no browser | PingOne, PingFederate | JWT | Human must complete device flow at enrollment | +| JWT bearer (RFC 7523) | Agent-to-agent trust with pre-established keys | PingFederate | JWT assertion | Agent presents a JWT signed with its private key; AS issues access token | + +--- + +## Pattern 6: Human-in-the-loop (HITL) approvals via CIBA + +For high-risk agent actions (fund transfers, privileged access grants), the agent should pause execution and request explicit human approval before proceeding — without requiring the human to be present in the original session. + +**CIBA (Client Initiated Backchannel Authentication)** is the OAuth 2.0 pattern for this: + +``` +AI Agent → CIBA authorization request (POST /bc-authorize) + → Ping AS sends push notification to user's mobile app + → User approves or denies on device + → Ping AS returns approval signal to agent +AI Agent → Token exchange (RFC 8693) for elevated token +AI Agent → Performs high-risk action with HITL-approved token +``` + +| HITL scenario | Mechanism | +|---|---| +| Fund transfer approval | Agent pauses; CIBA push to approver (user or manager); agent proceeds only on explicit approval | +| Privileged access grant | Same pattern; approver may be the user or a separate admin identity | +| Account disable (admin) | Full admin re-authentication; not delegation | + +**CIBA support:** PingFederate (native CIBA endpoint); PingOne AIC (Journey-based backchannel auth). CIBA requires a registered push delivery mechanism (PingID mobile, APNs/FCM push) on the approver's device. + +**Constraint:** CIBA polling timeout must be shorter than the agent's operation timeout. If the human does not respond within the CIBA timeout, the authorization is abandoned — the agent must handle this as a failure, not a retry. + +## Pattern 7: Bot and agent detection + +PingOne Protect's **bot detection predictor** explicitly identifies agentic AI automation, CUAs (computer-using agents), and automated frameworks as high-risk activity. This enables flows to: +- Block agents masquerading as human users +- Flag unexpected agentic activity from known human sessions +- Identify specific agent types in the risk evaluation response + +**Integration:** Wire Protect into the authentication flow for any application that handles both human and agent traffic. Branch on `result.recommendation.value`: +- `ALLOW` — proceed normally +- `CHALLENGE` — step-up (or CIBA approval for agents) +- `BLOCK` — deny; agent should not be authenticating through a human flow + +**For legitimate non-human flows:** Register agents via `private_key_jwt` client credentials — they bypass the Protect flow entirely. Bot detection fires when an agent incorrectly uses a human-facing authentication endpoint. + +See `ping-universal-services` → `protect-configuration.md` for the full bot detection predictor configuration. + +--- + +## Anti-patterns + +| Anti-pattern | Risk | Correct alternative | +|---|---|---| +| Long-lived API keys embedded in agent code or config | Key compromise is undetectable; blast radius is unlimited until manual rotation | `private_key_jwt` or mTLS with short-lived tokens | +| Shared token across multiple agent instances | One compromised instance exposes all | One client registration per agent instance or agent type | +| No scope restrictions on agent token | Compromised agent can perform any action the AS allows | Enumerate exact required scopes at registration | +| Refresh tokens with no rotation | Stolen refresh token grants indefinite access | Enable refresh token rotation; short absolute lifetime | +| No `request_id` correlation | Cannot attribute agent actions in audit logs | Add `X-Request-ID` or use `jti` in request-bound tokens | + +--- + +## Prerequisites + +- A PingOne environment (MT or AIC) or PingFederate deployment with an OAuth 2.0 AS configured. +- An application registered in the Ping AS with `client_credentials` grant type enabled. +- For `private_key_jwt`: a JWKS URI registered on the client; the agent holds the corresponding private key. +- For mTLS: X.509 certificate provisioned for the agent; PingFederate mTLS endpoint enabled. +- Resource servers must support Bearer token validation (JWT introspection or local JWT verification with JWKS). + +--- + +## Common variants + +| Variant | Notes | +|---|---| +| PingOne MT | Client credentials supported natively; JWKS URI registration available in the app settings | +| PingFederate | Supports `private_key_jwt`, mTLS, and RFC 7523 JWT bearer; dynamic client registration available for agent self-enrollment | +| PingOne AIC | OAuth AS provided by the AIC tenant; same client credentials flow; token introspection endpoint available | +| Multi-agent mesh | Each agent pair has a dedicated client registration; tokens are not shared; each agent's scope is narrowly tailored | + +--- + +## Related references + +- `references/curated/identity-for-ai-overview.md` +- `references/curated/verified-trust-overview.md` +- `references/curated/workforce-helpdesk-ai.md` + +## Source + +- https://docs.pingidentity.com/pingone/ai_agents/p1_ai_agents.html +- https://docs.pingidentity.com/pingoneaic/release-notes/rapid-channel/ai-agents.html +- https://docs.pingidentity.com/pingoneaic/release-notes/rapid-channel/ai-agents-configure-on-behalf-of-authentication-flow.html +- https://docs.pingidentity.com/pingoneaic/release-notes/rapid-channel/ai-agents-configure-dcr-onboarding-flow.html +- https://docs.pingidentity.com/pingone/threat_protection_using_pingone_protect/p1_protect_risk_predictors.html +- https://datatracker.ietf.org/doc/html/rfc8693 +- https://datatracker.ietf.org/doc/html/rfc7523 diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/identity-for-ai-overview.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/identity-for-ai-overview.md new file mode 100644 index 0000000..1c6880c --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/identity-for-ai-overview.md @@ -0,0 +1,181 @@ +--- +title: "Identity for AI Overview — Ping Identity" +product_family: cross-platform +products: ["pingone", "pingone-aic", "pingone-davinci"] +capabilities: ["identity-for-ai"] +services: [] +audience: ["developer", "architect"] +use_cases: ["ai-identity"] +doc_type: concept +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/solution-guides/identity-for-ai/identity-for-ai-solutions.html" +--- + +# Identity for AI Overview — Ping Identity + +Conceptual overview of Ping Identity's Identity for AI solution: three buckets of capability that secure AI workloads from agent identity through end-user authentication of AI-powered applications. + +## Scope + +Covers: the three sub-areas of Identity for AI, how they map to Ping products, the intent-to-sub-area routing table, and how this skill relates to the five other Ping Identity skills. +Does NOT cover: general platform setup (see `ping-foundation`), user journey design (see `ping-orchestration`), or standard app registration (see `ping-app-integration`). + +--- + +## The five pillars of Identity for AI + +Ping Identity's Identity for AI solution covers five distinct problem areas. Each has a distinct trust boundary and requires different Ping capabilities. + +| Pillar | What it means | Primary Ping surface | +|---|---|---| +| **Agent Identity** | Registering AI agents as first-class OAuth 2.0 identities — with unique credentials, lifecycle management, and ownership tracking — so agents are managed identities, not anonymous service accounts | PingOne (AI Agents feature), PingOne AIC (`/aiagent/register` DCR endpoint), PingFederate (dynamic client registration) | +| **Agent Security** | Securing what agents can do after they authenticate — delegated access, scoped tokens, least-privilege controls, and token exchange so agents act on behalf of users through delegation, not impersonation | PingOne OAuth 2.0 AS, PingFederate OAuth AS, PingOne AIC | +| **Agent Gateway** | Protecting the MCP servers, APIs, and resources agents call at runtime — validating requests, enforcing policy, throttling, and creating centralized audit trails before traffic reaches backend tools | PingGateway Agent Gateway module (MCP security gateway) | +| **Agent Detection** | Detecting and responding to suspicious agent behavior — using Protect's bot detection predictor, which explicitly identifies agentic AI automation, CUAs, and automated frameworks | PingOne Protect (bot detection predictor) | +| **AI App Authentication + Verified Trust** | Authenticating end-users of AI-powered apps (LLM chat interfaces, copilots), delegating access on their behalf, and embedding cryptographically verifiable trust signals that cross organizational boundaries | PingOne MT, PingOne AIC (Journey), DaVinci, PingOne Credentials | + +--- + +## How this skill differs from the other five + +| Skill | What it handles | Not handled here | +|---|---|---| +| `ping-quickstart` | Orientation and platform selection for new deployments | AI-specific identity patterns | +| `ping-foundation` | Platform administration, environments, directories, applications | Machine identity for AI workloads | +| `ping-orchestration` | Journey / DaVinci flow design for human authentication | Agent-to-API auth, Verified Trust | +| `ping-universal-services` | Protect, Verify, Credentials, Authorize as standalone services | AI agent lifecycle | +| `ping-app-integration` | SDK and OIDC library integration for traditional apps | AI agent token scoping, Verified Trust | +| **`ping-identity-for-ai`** (this skill) | All three AI identity buckets: Verified Trust, Agent Security, AI App Auth | Standard human identity administration | + +--- + +## Intent-to-pillar routing table + +| User intent | Pillar | Curated anchor | +|---|---|---| +| "Give my AI agent an identity so it can call our APIs" | Agent Identity + Security | `references/curated/agent-security-patterns.md` | +| "Register my AI agent as a managed identity in PingOne / AIC" | Agent Identity | `references/curated/agent-security-patterns.md` | +| "Use client credentials flow for an autonomous agent" | Agent Security | `references/curated/agent-security-patterns.md` | +| "Short-lived tokens, rotation, revocation for agents" | Agent Security | `references/curated/agent-security-patterns.md` | +| "Protect / secure an MCP server" | Agent Gateway | `references/curated/agent-gateway-mcp.md` | +| "PingGateway as MCP gateway" | Agent Gateway | `references/curated/agent-gateway-mcp.md` | +| "Detect agentic AI or bot activity in my flows" | Agent Detection | `references/curated/agent-security-patterns.md` + `ping-universal-services` Protect skill | +| "Apply Verified Trust signals / verifiable credentials" | Verified Trust | `references/curated/verified-trust-overview.md` | +| "Issue a verifiable credential for an AI agent" | Verified Trust | `references/curated/verified-trust-overview.md` | +| "Workforce helpdesk AI — delegation + step-up" | AI App Auth + Delegation | `references/curated/workforce-helpdesk-ai.md` | +| "Delegated token for AI assistant acting on behalf of user" | AI App Auth + Delegation | `references/curated/workforce-helpdesk-ai.md` | +| "Identity for AI architecture overview / strategy" | Overview | `references/curated/identity-for-ai-overview.md` | +| "Identity proofing outcome as trust signal for AI" | Verified Trust | `references/curated/verified-trust-overview.md` | + +--- + +## Relevant Ping products by pillar + +### Agent Identity +- **PingOne** — AI Agents feature; register agents as first-class OAuth 2.0 identities with lifecycle management. License: Agent IAM Core (contact Ping Sales). +- **PingOne AIC** — `/aiagent/register` DCR endpoint for dynamic agent onboarding; AI Agents admin UI in realm left-nav. **Available on Rapid channel as of 2026-06-03; Regular channel promotion planned.** +- **PingFederate** — Dynamic client registration (DCR) for agent self-enrollment at runtime. + +### Agent Security +- **PingOne** — OAuth 2.0 AS for client credential grants; Worker application registration; scoped tokens. +- **PingFederate** — OAuth 2.0 AS; supports `private_key_jwt`, mTLS, dynamic client registration, RFC 7523 JWT bearer. +- **PingOne AIC** — Journey-based step-up for agents requiring human approval (CIBA); OAuth AS for AIC workloads. + +### Agent Gateway +- **PingGateway 2025.11.1+ / 2026.x** — Agent Gateway module (McpAuditFilter, McpProtectionFilter, McpValidationFilter); acts as OAuth 2.0 RS in front of MCP servers; requires RFC 8707 on the AS. + +### Agent Detection +- **PingOne Protect** — Bot detection predictor explicitly identifies agentic AI automation, CUAs, and automated frameworks; returns HIGH risk + bot-specific agent type in the evaluation response. + +### Verified Trust + AI App Authentication +- **PingOne DaVinci** — Verified Trust flow connector (DaVinci Advanced license required); issues/verifies signed trust assertions. +- **PingOne AIC (Journey)** — Journey nodes for credential issuance and verification. +- **PingOne Credentials** — W3C Verifiable Credential wallet, issuance, revocation. +- **PingOne MT / AIC** — OIDC provider for end-user authentication of AI-powered apps; delegation via RFC 8693 token exchange. + +--- + +## Key concepts and terminology + +| Term | Definition | +|---|---| +| **AI agent** | An autonomous or semi-autonomous software component that acts on behalf of a user or organization, calls APIs, and makes decisions without continuous human oversight | +| **Verified Trust signal** | A digitally signed, cryptographically verifiable assertion about a subject (agent, user, device) that a relying party can verify offline | +| **Delegated token** | An OAuth 2.0 access token issued under RFC 8693 token exchange; carries both the user's identity (`sub`) and the agent's identity (`act.sub`) | +| **Machine-to-machine (M2M) auth** | Authentication between two software systems with no human in the loop; realized via the OAuth 2.0 client credentials grant | +| **Trust boundary** | The point at which one system stops trusting another system implicitly and must verify a presented credential or assertion | +| **Token scoping** | The practice of issuing access tokens with only the permissions (scopes) the requesting party needs for a specific operation | +| **Verifiable credential (VC)** | A W3C-standardized, tamper-evident credential that can be cryptographically verified by any party without calling back to the issuer | +| **Step-up authentication** | An additional authentication challenge requested mid-session when a higher assurance level is required (e.g., before a destructive action) | +| **Prompt injection** | An attack where malicious input to an LLM manipulates the agent's behavior; identity controls must be enforced in code, not delegated to the LLM | + +--- + +## Decision guide: which pillar do you need? + +**Is the question about registering and managing the agent as a digital identity?** +→ Agent Identity (`agent-security-patterns.md` — registration section) + +**Is the question about what the agent can access and how tokens are scoped/rotated/revoked?** +→ Agent Security (`agent-security-patterns.md`) + +**Is the question about protecting an MCP server or API that agents call?** +→ Agent Gateway (`agent-gateway-mcp.md`) + +**Is the question about detecting suspicious or unexpected agentic activity in flows?** +→ Agent Detection → `ping-universal-services` (Protect bot detection predictor) + +**Is the question about a human user involved in the flow?** +- Agent acts on the user's behalf → AI App Auth + Delegation (`workforce-helpdesk-ai.md`) +- Agent presents claims across org boundaries → Verified Trust (`verified-trust-overview.md`) + +**Is the primary concern portability of claims across organizational boundaries?** +→ Verified Trust (`verified-trust-overview.md`) + +**Is the integration a standard OIDC app without AI-specific patterns?** +→ Route to `ping-foundation` or `ping-app-integration`, not this skill + +--- + +## Cross-skill orchestration + +A production AI identity solution composes multiple skills in sequence. See the routing table in `SKILL.md` (Multi-skill use cases section) for the canonical four-step sequence: `ping-foundation` → `ping-identity-for-ai` → `ping-orchestration` → `ping-app-integration`. + +--- + +## Prerequisites + +- A PingOne environment (MT or AIC tenant) or a PingFederate deployment must already be provisioned. +- For Verified Trust: DaVinci license with the Verified Trust connector; PingOne Credentials tenant configured. +- For Agent Security: An OAuth 2.0 AS reachable by the agent's runtime environment. +- For AI App Authentication: An OIDC application registration in the chosen Ping platform. + +--- + +## Common variants + +| Variant | Notes | +|---|---| +| Cloud-native (PingOne MT) | Fully managed OAuth AS; DaVinci for orchestration; fastest to start | +| AIC (PingOne ST lineage) | Journey nodes for Verified Trust; PingOne Credentials wallet integration | +| On-premises (PingFederate) | PingFederate OAuth AS; dynamic client registration available for agent self-registration | +| Hybrid | PingFederate federates to PingOne; agents get tokens from PingFederate, Verified Trust from DaVinci | + +--- + +## Related references + +- `references/curated/agent-security-patterns.md` +- `references/curated/verified-trust-overview.md` +- `references/curated/workforce-helpdesk-ai.md` + +## Source + +- https://docs.pingidentity.com/solution-guides/identity-for-ai/identity-for-ai-solutions.html +- https://docs.pingidentity.com/pingone/ai_agents/p1_ai_agents.html +- https://docs.pingidentity.com/pingoneaic/release-notes/rapid-channel/ai-agents.html +- https://docs.pingidentity.com/davinci/applications/davinci_applications.html +- https://docs.pingidentity.com/pinggateway/2026/mcp/index.html +- https://developer.pingidentity.com/identity-for-ai/release-notes/idai-whats-new.html diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/verified-trust-overview.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/verified-trust-overview.md new file mode 100644 index 0000000..313d96d --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/verified-trust-overview.md @@ -0,0 +1,171 @@ +--- +title: "Verified Trust Overview — Ping Identity" +product_family: cross-platform +products: ["pingone-davinci", "pingone-aic"] +capabilities: ["identity-for-ai", "universal-services"] +services: ["credentials"] +audience: ["developer", "architect"] +use_cases: ["ai-identity"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/davinci/applications/davinci_applications.html" +--- + +# Verified Trust Overview — Ping Identity + +Reference for Ping Identity's Verified Trust capabilities: digitally signed trust assertions and verifiable credentials that allow AI agents and applications to carry cryptographically verifiable claims about identity, device posture, and organizational membership. + +## Scope + +Covers: what Verified Trust is, the trust model (issuer → holder → verifier), trust signal types and carrier formats, Ping product integration points, configuration constraints, and licensing requirements. +Does NOT cover: the general OAuth 2.0 / OIDC agent security pattern (see `references/curated/agent-security-patterns.md`), standard DaVinci flow authoring (see `ping-orchestration`), or end-user identity proofing without a credential output (see `ping-universal-services`). + +--- + +## What Verified Trust is + +Verified Trust is a framework for embedding cryptographically verifiable claims into the interactions between AI agents, applications, and protected resources. A trust signal is a signed assertion that a verifier can check without calling back to the issuer — enabling offline or low-latency trust decisions at the edge or inside an AI agent runtime. + +Verified Trust addresses a specific gap in standard OAuth 2.0: an access token confirms that an authorization server issued it, but does not carry rich claims about the presenting entity's device health, organizational role, or identity-proofing outcome in a way that is portable and independently verifiable by third parties. + +--- + +## Trust model + +``` +Issuer (Ping DaVinci / Credentials) + │ + │ issues signed credential / trust signal + ▼ +Holder (AI agent, user wallet, application) + │ + │ presents credential / trust signal + ▼ +Verifier (API gateway, resource server, another AI agent) +``` + +| Actor | Role | Ping surface | +|---|---|---| +| **Issuer** | Creates and signs the credential or trust signal | PingOne Credentials issuer, DaVinci Verified Trust connector | +| **Holder** | Stores and presents the credential | PingOne Credentials wallet, agent runtime, app SDK | +| **Verifier** | Checks the signature and claims | DaVinci Verified Trust verifier connector, API gateway policy, custom middleware | + +--- + +## Trust signal types and carrier formats + +| Trust signal type | What it carries | Carrier format | Ping product | Typical use case | +|---|---|---|---|---| +| Identity proofing outcome | Result of document or biometric verification (pass/fail, assurance level) | W3C Verifiable Credential (JWT-VC) | PingOne Verify → PingOne Credentials | AI agent confirms user passed identity proofing before performing privileged action | +| Device posture | Device health score, MDM enrollment status, OS patch level | Signed JSON assertion (DaVinci) | PingOne Protect → DaVinci | Deny agent API request if device posture drops below threshold | +| Organizational claim | Membership in an org, department, or role — externally verifiable | W3C Verifiable Credential (JSON-LD or JWT-VC) | PingOne Credentials | Federated agent-to-agent trust across organizational boundaries | +| Session context | Risk score, MFA completeness, session assurance level at time of issuance | Signed JWT (DPoP-bound or plain) | PingOne DaVinci | Carry session quality into downstream API calls without re-authenticating | +| Custom attribute | Arbitrary business claim (clearance level, regulatory jurisdiction) | W3C Verifiable Credential (JWT-VC) | PingOne Credentials | Regulatory compliance assertions carried by an AI agent into a regulated API | + +--- + +## Integration points in the Ping platform + +### DaVinci Verified Trust connector + +The DaVinci Verified Trust connector provides both issuance and verification within an orchestration flow. + +| Connector mode | What it does | Required configuration | +|---|---|---| +| Issuer | Creates and signs a trust signal for an authenticated subject | Signing key reference, claim mapping, expiry window | +| Verifier | Validates an inbound trust signal and extracts claims | Issuer DID or public key, accepted claim types, revocation check flag | + +**Constraint:** The Verified Trust connector requires the DaVinci Advanced license tier. It is not available on the DaVinci Base license. + +### Journey node (PingOne AIC) + +PingOne AIC provides a Journey node for credential issuance and verification as part of an authentication tree. + +| Node type | Purpose | +|---|---| +| Credential Issuer node | Issues a W3C Verifiable Credential after successful Journey completion | +| Credential Verifier node | Validates a presented credential before granting a Journey outcome | + +**Constraint:** Journey credential nodes require PingOne Credentials to be configured and linked to the AIC tenant. + +### PingOne Credentials + +PingOne Credentials manages the full verifiable credential lifecycle: + +| Lifecycle stage | API surface | Key constraint | +|---|---|---| +| Credential type definition | Credentials admin API — define schema, claim types, expiry rules | Schema must be defined before issuance; changes require a new credential type version | +| Issuance | Credentials issuance API — issue a credential to a wallet address or QR-code delivery | Wallet must be activated by the holder before credential can be delivered | +| Presentation | Holder presents via OpenID4VP or DIDComm | Verifier must support the same transport | +| Revocation | Credentials revocation API | Revocation check adds ~50–100 ms latency; cache TTL controls revocation freshness | + +--- + +## Configuration field reference + +### Verified Trust connector — issuer mode + +| Field | Type | Constraint | +|---|---|---| +| Signing key | Key reference | Must reference a key in the DaVinci key store; RS256 or ES256 supported | +| Subject claim mapping | Claim map | Maps DaVinci flow variables to VC subject claims | +| Expiry (seconds) | Integer | Maximum 86 400 (24 hours); shorter is recommended for agent tokens | +| Credential type | String | Must match a registered credential type in PingOne Credentials if VC format is used | +| Revocation | Boolean | Enables revocation list entry on issuance; requires PingOne Credentials | + +### Verified Trust connector — verifier mode + +| Field | Type | Constraint | +|---|---|---| +| Trusted issuers | List of DID / JWKS URIs | At least one issuer required; wildcard not supported | +| Accepted credential types | String list | Must match the `type` array in the presented credential | +| Revocation check | Boolean | Adds external call to revocation registry; disable only for low-assurance use cases | +| Clock skew tolerance (seconds) | Integer | Default 60; increase for cross-region deployments | + +--- + +## Licensing and availability constraints + +| Capability | License requirement | +|---|---| +| DaVinci Verified Trust connector (issuer or verifier) | DaVinci Advanced | +| PingOne Credentials (wallet, issuance, revocation) | PingOne Credentials add-on | +| AIC Journey credential nodes | PingOne AIC + PingOne Credentials add-on | +| PingOne Verify (identity proofing outcome as input) | PingOne Verify add-on | + +--- + +## Prerequisites + +- DaVinci environment with the Advanced license activated. +- At least one signing key configured in the DaVinci key store. +- If issuing W3C Verifiable Credentials: PingOne Credentials tenant provisioned and linked. +- If using identity proofing outcomes as trust signal inputs: PingOne Verify configured and a verification policy defined. +- Verifier must have network access to the issuer's JWKS URI or the DID resolver if using DID-based key resolution. + +--- + +## Common variants + +| Variant | Notes | +|---|---| +| Agent-to-API (signed assertion) | Agent carries a DaVinci-issued signed JWT; API gateway verifies signature and claims inline — no credential wallet required | +| User-held VC (wallet delivery) | User identity proofing outcome issued as a W3C VC to a PingOne Credentials wallet; AI agent requests presentation before performing privileged action | +| Cross-org trust | Two organizations agree on a shared credential schema; each issues credentials under their own DID; verifiers trust both DIDs | +| Offline / edge verification | Trust signal is verified at the edge without calling back to the issuer; requires short expiry and local key cache | + +--- + +## Related references + +- `references/curated/identity-for-ai-overview.md` +- `references/curated/agent-security-patterns.md` +- `references/curated/workforce-helpdesk-ai.md` + +## Source + +[PingOne DaVinci Verified Trust connector](https://docs.pingidentity.com/davinci/verified-trust) +[PingOne Credentials documentation](https://docs.pingidentity.com/pingone/credentials) +[W3C Verifiable Credentials Data Model](https://www.w3.org/TR/vc-data-model/) diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/workforce-helpdesk-ai.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/workforce-helpdesk-ai.md new file mode 100644 index 0000000..90ce582 --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/curated/workforce-helpdesk-ai.md @@ -0,0 +1,189 @@ +--- +title: "Workforce Helpdesk AI — Identity Pattern for AI Assistants Acting on Behalf of Employees" +product_family: cross-platform +products: ["pingone", "pingone-aic"] +capabilities: ["identity-for-ai", "orchestration"] +services: [] +audience: ["architect", "developer"] +use_cases: ["workforce"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/use_cases/p1_oauth_2_token_exchange.html" +--- + +# Workforce Helpdesk AI — Identity Pattern for AI Assistants Acting on Behalf of Employees + +Identity pattern for workforce helpdesk AI: an AI assistant that handles employee requests (password reset, access provisioning, policy questions) on behalf of an authenticated user, using a delegated token rather than the user's direct credential. + +## Scope + +Covers: the delegation model, the verification pattern before high-risk actions, the audit trail requirement, and a sequence table mapping each step to the responsible actor and Ping product. +Does NOT cover: machine-to-machine auth without a human user (see `references/curated/agent-security-patterns.md`), Verified Trust signal issuance (see `references/curated/verified-trust-overview.md`), or standard workforce SSO setup (see `ping-foundation`). + +--- + +## Core principle: the agent must never hold the user's credential + +The AI helpdesk agent does not know the user's password, PIN, or session cookie. It receives a delegated access token — issued by Ping after the user authenticates — that carries the user's identity claims and the specific scopes the agent is authorized to exercise on the user's behalf. + +This separation enforces: +- **Non-repudiation**: all actions are attributable to the user (who authorized the delegation) and the agent (which performed the action). +- **Revocability**: revoking the delegated token immediately stops the agent; the user's underlying session is unaffected. +- **Minimal privilege**: the delegated token carries only the scopes the agent needs — the user's full access rights are not transferred. + +--- + +## The delegation model + +### Step-by-step flow + +``` +1. User authenticates to Ping (PingOne / AIC Journey) + │ + ▼ +2. Ping issues a user access token (short-lived, user scopes) + │ + ▼ +3. AI helpdesk app requests a delegated token via OAuth 2.0 + Token Exchange (RFC 8693): + subject_token = user access token + requested_token_type = access_token + scope = helpdesk:read helpdesk:provision + │ + ▼ +4. Ping AS issues a delegated token: + sub = user identity + act.sub = agent client_id (RFC 8693 actor claim) + scope = helpdesk:read helpdesk:provision + exp = short TTL (15–30 minutes recommended) + │ + ▼ +5. Agent calls directory / provisioning APIs with delegated token + │ + ▼ +6. Resource server validates token, extracts user + agent claims, + logs action with both identities +``` + +### Delegation token claims + +| Claim | Value | Purpose | +|---|---|---| +| `sub` | User's unique identifier (PingOne user ID or employee ID) | Identifies who the action is being performed for | +| `act.sub` | Agent's `client_id` | Identifies which agent performed the action (RFC 8693) | +| `scope` | Exactly the scopes needed by the agent | Enforces minimal privilege | +| `exp` | UTC epoch; short TTL | Limits the window of a compromised token | +| `jti` | UUID | Correlation ID for audit; unique per token issuance | +| `iss` | Ping AS URL | Allows resource server to verify token origin | + +--- + +## Verification pattern before high-risk actions + +Not all helpdesk actions carry the same risk. A tiered verification model reduces friction for low-risk requests while enforcing strong assurance before destructive operations. + +### Risk tier classification + +| Action | Risk tier | Verification required | +|---|---|---| +| Answer a policy question | Low | Delegated token (user already authenticated) | +| View own profile or group membership | Low | Delegated token | +| Reset own non-privileged app password | Medium | MFA step-up (DaVinci or Journey step-up flow) | +| Provision access to a new application | Medium–High | MFA step-up + manager approval (if policy requires) | +| Reset account password (Ping-managed) | High | MFA step-up + re-verification of email or phone | +| Grant privileged / admin access | High | Step-up with FIDO2 / hardware key + additional approval | +| Disable another user's account (admin scenario) | High | Admin re-authentication, not delegation | + +**Constraint:** MFA step-up must be triggered by the AI application, not decided by the AI model. The decision tree above must be encoded in application logic or a DaVinci flow, not left to the LLM's judgment. + +### Step-up verification using DaVinci or Journey + +The AI application redirects (or presents an embedded widget for) the user to a Ping step-up endpoint: +- **DaVinci:** Use a DaVinci flow with the appropriate MFA connector; on success, DaVinci issues a step-up assertion or a new elevated token. +- **AIC Journey:** Use an auth level upgrade Journey (ACR value `urn:pingidentity:assurance:2FA`). + +After step-up is confirmed, the AI application requests a new delegated token with the elevated scope. The original delegated token without elevated scope must not be used for high-risk actions. + +--- + +## Sequence summary table + +| Step | Actor | Ping product | Output | +|---|---|---|---| +| 1. User authentication | End user (employee) | PingOne MT / PingOne AIC | User session; user access token | +| 2. Helpdesk app token request | AI helpdesk application (front-end) | PingOne AS / AIC OAuth AS | Delegated access token (RFC 8693 token exchange) | +| 3. Low-risk request execution | AI agent (back-end) | Directory API / provisioning API | Action result; audit log entry | +| 4. Risk-tier evaluation | AI application logic | (No Ping product — in-app decision) | Tier classification; step-up decision | +| 5. Step-up MFA (medium/high actions) | End user | PingOne DaVinci / AIC Journey | Step-up confirmation; elevated token | +| 6. High-risk action execution | AI agent (back-end) | Directory API / provisioning API | Action result; audit log entry with step-up evidence | +| 7. Token expiry / session end | Ping AS | PingOne AS / AIC OAuth AS | Token revocation at TTL or explicit logout | + +--- + +## Audit trail requirements + +Every action taken by the AI agent on behalf of a user must produce an audit record with the following fields. + +| Audit field | Source | Constraint | +|---|---|---| +| `user_id` | `sub` claim in delegated token | Must be the user's stable identifier, not a session ID | +| `agent_id` | `act.sub` claim (RFC 8693) or `client_id` | Must uniquely identify the agent component that performed the action | +| `action` | API endpoint + HTTP method + resource identifier | Resource server logs this at the time of the call | +| `authorization_evidence` | Token `jti` + step-up assertion `jti` (if applicable) | Enables reconstruction of the full authorization chain in audit | +| `timestamp` | UTC ISO 8601 from resource server clock | Must not use the token `iat`; use the time of the API call | +| `outcome` | Success / failure / partial | Required for incident investigation | +| `request_id` | `X-Request-ID` header sent by agent | Correlates multi-step operations (e.g., provision + notify) | + +**Key constraint:** The AI agent must not strip or modify authorization headers before forwarding calls to downstream APIs. The delegated token must reach the resource server intact so that the resource server can log both user and agent identities. + +--- + +## Constraints and guardrails + +| Constraint | Rationale | +|---|---| +| Agent must never store user credentials (password, PIN, session token) | Prevents credential theft via agent compromise | +| Delegated token TTL must be ≤ the user session TTL | Token must not outlive the session that authorized it | +| Delegated token scope must be a strict subset of the user's scopes | Agent cannot be granted more than the user has | +| Step-up decision must be in application code, not the LLM | LLMs may be manipulated via prompt injection; security decisions must be deterministic | +| Delegated token must not be passed to sub-agents without explicit re-authorization | Lateral movement risk; each agent component must have its own token | +| Audit records must be immutable and stored outside the AI system | Prevents tampering by a compromised agent | + +--- + +## Prerequisites + +- PingOne environment or AIC tenant with OAuth 2.0 token exchange (RFC 8693) enabled on the Authorization Server. +- AI helpdesk application registered as a confidential client with `urn:ietf:params:oauth:grant-type:token-exchange` grant type. +- User-facing authentication configured (Journey or PingOne sign-on policy) with the required MFA methods for step-up. +- Directory and provisioning APIs configured to accept and validate delegated tokens. +- Audit log sink configured to receive structured log events from resource servers. + +--- + +## Common variants + +| Variant | Notes | +|---|---| +| Password reset only | Simplest variant; agent only calls the directory reset API; medium-risk tier requires MFA step-up | +| Full access provisioning | Adds approval workflow; token exchange includes `policy:provisioning` scope; approval step may require a DaVinci connector to an ITSM system | +| Multi-LLM pipeline (orchestrator + sub-agents) | Each sub-agent receives its own narrowly scoped delegated token; orchestrator must not forward its token to sub-agents | +| External identity provider (federated workforce) | User authenticates via enterprise IdP (SAML/OIDC federation to PingOne); PingOne issues a local token after federation; token exchange proceeds as normal | +| AIC-native deployment | Token exchange uses AIC's built-in OAuth AS; step-up uses AIC Journey with ACR-based auth level upgrade | + +--- + +## Related references + +- `references/curated/identity-for-ai-overview.md` +- `references/curated/agent-security-patterns.md` +- `references/curated/verified-trust-overview.md` + +## Source + +[RFC 8693 — OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693) +[PingOne Token Exchange documentation](https://docs.pingidentity.com/pingone/token-exchange) +[PingOne AIC OAuth 2.0 guide](https://docs.pingidentity.com/aic/oauth2) +[PingOne DaVinci step-up MFA](https://docs.pingidentity.com/davinci/step-up-mfa) diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/generated/.gitkeep b/plugins/ping-identity/skills/ping-identity-for-ai/references/generated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/generated/ai-identity/top-20.json b/plugins/ping-identity/skills/ping-identity-for-ai/references/generated/ai-identity/top-20.json new file mode 100644 index 0000000..406fb29 --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/generated/ai-identity/top-20.json @@ -0,0 +1,77 @@ +{ + "_comment": "Machine-generated. Do not hand-edit. Regenerated by CI workflow build-reference-manifests.yml on docs publish.", + "skill": "ping-identity-for-ai", + "branch": "ai-identity", + "generated_at": "2026-06-03T12:47:32Z", + "max_docs": 20, + "docs": [ + { + "title": "Agent Gateway \u2014 Securing MCP Servers with PingGateway", + "slug": "plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-gateway-mcp.md", + "url": "https://docs.pingidentity.com/pinggateway/2026/mcp/index.html", + "doc_type": "guide", + "capabilities": [ + "identity-for-ai" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Agent Security Patterns \u2014 Securing AI Agents with Ping Identity", + "slug": "plugins/ping-identity/skills/ping-identity-for-ai/references/curated/agent-security-patterns.md", + "url": "https://docs.pingidentity.com/pingone/ai_agents/p1_ai_agents.html", + "doc_type": "guide", + "capabilities": [ + "identity-for-ai", + "foundation" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Workforce Helpdesk AI \u2014 Identity Pattern for AI Assistants Acting on Behalf of Employees", + "slug": "plugins/ping-identity/skills/ping-identity-for-ai/references/curated/workforce-helpdesk-ai.md", + "url": "https://docs.pingidentity.com/pingone/use_cases/p1_oauth_2_token_exchange.html", + "doc_type": "guide", + "capabilities": [ + "identity-for-ai", + "orchestration" + ], + "score": 1.0, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Verified Trust Overview \u2014 Ping Identity", + "slug": "plugins/ping-identity/skills/ping-identity-for-ai/references/curated/verified-trust-overview.md", + "url": "https://docs.pingidentity.com/davinci/applications/davinci_applications.html", + "doc_type": "reference", + "capabilities": [ + "identity-for-ai", + "universal-services" + ], + "score": 0.95, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + }, + { + "title": "Identity for AI Overview \u2014 Ping Identity", + "slug": "plugins/ping-identity/skills/ping-identity-for-ai/references/curated/identity-for-ai-overview.md", + "url": "https://docs.pingidentity.com/solution-guides/identity-for-ai/identity-for-ai-solutions.html", + "doc_type": "concept", + "capabilities": [ + "identity-for-ai" + ], + "score": 0.9, + "canonical": true, + "last_updated": "2026-06-03", + "stale_warning": false + } + ] +} diff --git a/plugins/ping-identity/skills/ping-identity-for-ai/references/runtime/docs-mcp-routing.md b/plugins/ping-identity/skills/ping-identity-for-ai/references/runtime/docs-mcp-routing.md new file mode 100644 index 0000000..5a1be76 --- /dev/null +++ b/plugins/ping-identity/skills/ping-identity-for-ai/references/runtime/docs-mcp-routing.md @@ -0,0 +1,49 @@ +--- +title: Docs MCP routing — ping-identity-for-ai +product_family: cross-platform +capabilities: ["identity-for-ai"] +doc_type: reference +canonical: false +audience: ["developer", "architect"] +status: current +last_updated: 2026-05-29 +--- + +# Runtime tier — Docs MCP routing for ping-identity-for-ai + +This file describes when and how this skill falls back to live Docs MCP retrieval. It is the third tier in the strategy doc § 0 "Agent Path". + +## When to escalate to Docs MCP + +Use Docs MCP only when: +1. The 1–3 curated anchors loaded from `references/curated/` did not answer the question. +2. The bounded shortlist in `references/generated//top-N.json` did not fill the gap. +3. The user's task requires version-specific, current, or long-tail information. + +If any of these is false, do NOT call Docs MCP. Strategy doc § 0 mandates "use the smallest trusted context first." + +## Surgical query rules + +When Docs MCP is required, query it with: +- The exact platform family (PingOne MT, PingOne ST, Ping Software Suite) +- The exact product or service name +- The exact capability +- A version constraint when applicable + +Retrieve specific sections, not full page dumps. + +## Helix as a runtime path + +Production-bound execution runs through Helix conversation APIs. Helix is **not** a v1 skill; it is a runtime tier referenced from this file. + +### Sandbox vs production decision rule (apply in order) + +1. If the user asks for a plan, explanation, walkthrough, or evaluation help → `docs` mode. +2. If the user names a sandbox, trial, POC, or asks "how would I…" → `docs` mode. +3. If the user names a production tenant, asks to apply/create/update/configure in a specific tenant, or invokes a `/ping:` command with `--apply` → `helix` mode. +4. If the user is unclear, default to `docs` and ask one clarifying question: "Do you want a plan to apply manually, or should I execute this against a live tenant via Helix?" + +## Related + +- `references/curated/` — tier 1 +- `references/generated/` — tier 2 diff --git a/plugins/ping-identity/skills/ping-orchestration/SKILL.md b/plugins/ping-identity/skills/ping-orchestration/SKILL.md new file mode 100644 index 0000000..7f69b39 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/SKILL.md @@ -0,0 +1,120 @@ +--- +name: ping-orchestration +description: Use this skill whenever you need to design, build, plan, or advise on authentication flows, journeys, or orchestration logic for Ping Identity platforms — including DaVinci flows, PingOne ST journeys, PingAM trees, scripted decision nodes, and branching authentication/registration logic. When the intent is a platform or product comparison ("AIC vs DaVinci?", "journey vs DaVinci flow?", "which platform for my login flow?") without specifying the use case, workforce vs CIAM context, or platform, ask a clarifying question about the use case or platform before answering. When the platform is unspecified and matters (e.g., MFA configuration differs by platform), ask which platform before advising. Also invoke with /ping-orchestration. +compatibility: Designed for Ping Identity orchestration tasks. MCP tools for PingOne ST are used when available to create and update journeys directly. +metadata: + publisher: Ping Identity + version: "1.0" +--- + +# ping-orchestration + +Design and build authentication flows, orchestration logic, and journey-based experiences across Ping Identity platforms. MCP tools handle execution; this skill supplies design patterns, node sequencing, branching logic, and platform-specific constraints. + +## Invocation + +Invoke this skill explicitly with `/ping-orchestration` or by saying "use ping-orchestration to...". + +## When to use this skill + +Trigger on ANY question — including advisory, planning, and "what nodes do I need" requests, not just implementation — when the task involves: +- Building or designing a login, registration, recovery, MFA, or step-up journey in PingOne ST / AIC / PingAM +- Creating or designing a DaVinci flow for authentication, MFA, or orchestration +- Configuring a PingAM authentication tree or scripted decision node +- Planning or reviewing journey structure before implementation +- Deciding between inner journeys, scripted nodes, or DaVinci connectors +- Any question about designing, planning, or advising on authentication flows, journeys, or orchestration logic in PingOne ST, PingOne MT / DaVinci, or PingAM + +## When NOT to use this skill + +- If the platform is not yet set up (no tenant, no realm, no app registered): use `ping-foundation` first +- If the task is **configuring the platform layer** (apps, directories, policies, branding): use `ping-foundation` +- If the task is **invoking a Universal Service** (Protect, Verify, IGA, Credentials) without needing flow design: use `ping-universal-services` +- If the task is **integrating the flow into an app or SDK**: use `ping-app-integration` +- If unsure which platform: use `ping-quickstart` first + +## Multi-skill use cases + +Orchestration sits in the middle of the stack. Platform foundation must exist first; other skills extend what flows can do. + +| Sequence | Skill | +|---|---| +| Before: tenant, realm, identity store, app configured | `ping-foundation` | +| After: add risk scoring, MFA step-up, identity verification within the flow | `ping-universal-services` | +| After: wire the finished flow into a web, mobile, or SDK-based app | `ping-app-integration` | + +**Example — CIAM with proofing:** `ping-foundation` → `ping-orchestration` → `ping-universal-services` (Verify) → `ping-app-integration`. + +--- + +## MCP tool-first execution + +Scan available tools for MCP tools that can perform the required operation. If matching tools are available, use them directly. Otherwise, proceed with curated references. + +--- + +## Routing — Step 1: Which platform? + +| Platform signal | Branch | +|---|---| +| PingOne ST tenant, PingAM, identity cloud, ForgeRock lineage | [PingOne ST](#pingone-st) | +| PingOne MT + DaVinci | [PingOne MT / DaVinci](#pingone-mt--davinci) | + +--- + +## PingOne ST + +Sub-routing by task and journey use case: see `references/curated/pingone-st/routing-index.md`. + +**Quick reference — node families:** + +| Task | Reference | +|---|---| +| Journey design principles, patterns, resilience, security | `references/curated/pingone-st/journey-design-patterns.md` | +| Node composition rules, PageNode usage, child node gotchas | `references/curated/pingone-st/nodes/node-fundamentals.md` | +| Username/password, passthrough auth, session entry, lifecycle outcomes | `references/curated/pingone-st/nodes/basic-auth-nodes.md` | +| MFA: WebAuthn, OATH, push, OTP, recovery codes | `references/curated/pingone-st/nodes/mfa-nodes.md` | +| Risk scoring, lockout, CAPTCHA, auth level, PingOne Authorize | `references/curated/pingone-st/nodes/risk-management-nodes.md` | +| Registration, attributes, consent, KBA, T&C, social login, SelectIdP | `references/curated/pingone-st/nodes/identity-management-nodes.md` | +| Scripting, page composition, session, state, async, polling, LoginCount | `references/curated/pingone-st/nodes/utility-nodes.md` | +| SAML/OIDC federation, Twilio Verify, device/cookie/cert | `references/curated/pingone-st/nodes/federation-contextual-nodes.md` | + +**Generated shortlist** (fallback): +- `references/generated/pingone-st/top-25.json` + +--- + +## PingOne MT / DaVinci + +**Sub-routing by task:** + +| Task | Reference | +|---|---| +| DaVinci flow concepts, connectors, variables, versioning | `references/curated/pingone-mt/davinci-overview.md` | +| DaVinci flow design patterns (login, registration, step-up, error) | `references/curated/pingone-mt/davinci-flow-patterns.md` | +| DaVinci registration + email verification + MFA enrollment/step-up | `references/curated/pingone-mt/davinci-registration-and-mfa.md` | + +--- + +## Cross-platform orchestration patterns + +| Task | Reference | +|---|---| +| Passkeys / passwordless / FIDO2 design across PingOne MT, PingOne ST, Ping Software | `references/curated/cross-platform/passkeys-and-passwordless.md` | + +**Generated shortlist** (fallback): `references/generated/pingone-mt/top-25.json` + +--- + +## Retrieval escalation + +Load 1–3 curated anchors for the detected platform/task; stop if sufficient. If not, scan the generated shortlist; pull summaries only. + +## Cross-skill escalation + +| If the task also involves... | Reference skill | +|---|---| +| Platform setup not yet complete | `ping-foundation` | +| Shared services (Protect, Verify, IGA, Credentials) within the flow | `ping-universal-services` | +| App/SDK code integration | `ping-app-integration` | +| Platform selection or orientation | `ping-quickstart` | diff --git a/plugins/ping-identity/skills/ping-orchestration/ping-marketplace.json b/plugins/ping-identity/skills/ping-orchestration/ping-marketplace.json new file mode 100644 index 0000000..8cd0109 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/ping-marketplace.json @@ -0,0 +1,34 @@ +{ + "skill_id": "ping-orchestration", + "display_name": "Ping Identity Orchestration", + "description": "Design and build authentication flows, journeys, and orchestration logic. Covers PingOne ST journeys and authentication trees, DaVinci flows (PingOne MT), scripted decision nodes, inner journeys, and multi-step registration and login patterns.", + "version": "1.0.0", + "publisher": "Ping Identity", + "tags": { + "product_family": ["pingone-mt", "pingone-st"], + "products": [ + "pingone-st", + "pingam", + "davinci", + "pingone" + ], + "capabilities": ["orchestration"], + "audience": ["developer", "architect", "admin"], + "use_cases": ["workforce", "customer"], + "protocols": ["oidc", "oauth2", "saml"] + }, + "entry_point": "SKILL.md", + "references": { + "curated_path": "references/curated/", + "generated_path": "references/generated/" + }, + "related_skills": [ + "ping-foundation", + "ping-universal-services", + "ping-app-integration", + "ping-quickstart" + ], + "min_context_tokens": 500, + "max_curated_docs": 3, + "max_shortlist_docs": 25 +} diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/cross-platform/passkeys-and-passwordless.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/cross-platform/passkeys-and-passwordless.md new file mode 100644 index 0000000..ac01696 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/cross-platform/passkeys-and-passwordless.md @@ -0,0 +1,284 @@ +--- +title: "Passkeys and Passwordless Authentication" +product_family: cross-platform +products: ["pingone", "pingone-aic", "davinci", "pingam"] +capabilities: ["orchestration", "mfa", "passwordless"] +services: ["mfa"] +audience: ["developer", "architect", "admin"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-03" +slug: "https://docs.pingidentity.com/pingone/authentication/p1_passkeys_overview.html" +--- + +# Passkeys and Passwordless Authentication + +Design patterns for passkey (FIDO2 / WebAuthn) and passwordless flows across PingOne MT (DaVinci), PingOne ST (AIC journeys), and the Ping Software Suite. Covers registration, authentication, recovery, fallback, and the three friction tiers (low / balanced / higher-assurance). + +## Scope + +**Covers:** +- Passkey vs passwordless terminology and the WebAuthn ceremonies +- Registration patterns (inline at sign-up, deferred at next login, opportunistic upgrade) +- Authentication patterns (passkey-first, magic link, OTP-only, push-only) +- Friction tier matrix: low / balanced / higher-assurance +- Recovery and fallback when the passkey is unavailable +- Platform-specific node and connector references for AIC, DaVinci, and PingFederate + +**Does NOT cover:** +- WebAuthn protocol internals — see external W3C WebAuthn spec +- SDK-side passkey wiring — see `ping-app-integration` +- Risk-based step-up logic — see `references/curated/pingone-st/journey-use-cases/financial-services-step-up.md` (AIC) or DaVinci registration anchor +- Hardware key (YubiKey) procurement / lifecycle + +--- + +## Vocabulary + +| Term | Meaning | +|---|---| +| **WebAuthn** | W3C standard for browser-mediated cryptographic authentication | +| **FIDO2** | Authentication framework that includes WebAuthn + CTAP2; used interchangeably with WebAuthn for passkey discussions | +| **Passkey** | A FIDO2 credential, typically synced across the user's devices via the platform credential manager (Apple iCloud Keychain, Google Password Manager, 1Password) | +| **Device-bound credential** | FIDO2 credential that does not sync — tied to the device's secure enclave | +| **Discoverable credential** | A passkey the authenticator can list without the relying party providing a hint; enables "username-less" sign-in | +| **Resident key** | Older WebAuthn term for discoverable credential | +| **Passwordless** | Authentication without a knowledge factor (no password). Can use passkeys, magic links, OTP, or push | + +> **Rule:** "Passkey" implies WebAuthn / FIDO2. "Passwordless" is broader — it includes magic links and OTP-only flows that are not WebAuthn-based. + +--- + +## Friction tiers + +Use this matrix to match the assurance the user actually needs to the friction they will tolerate: + +| Tier | Primary factor | Step-up | Recovery | Best for | +|---|---|---|---|---| +| **Low friction** | Passkey (auto-fill, conditional UI) or magic link | None unless risk elevates | Email OTP, SMS OTP | Consumer apps; high abandonment risk; no high-value actions | +| **Balanced** | Passkey OR password+OTP, user choice | Step-up to MFA on sensitive action | Multi-channel (email + SMS); admin reset | Most CIAM apps; mixed-risk consumer apps | +| **Higher assurance** | Passkey (device-bound preferred) | Mandatory step-up for any privileged action | Admin-mediated; identity proofing (PingOne Verify) re-run on full reset | Workforce; financial; healthcare; regulated industries | + +**Friction tier choice drives:** +- Whether discoverable credentials (resident keys) are required at registration +- Whether device-bound (non-syncable) authenticators are mandatory +- Whether step-up is policy-driven (Protect risk score) or always-on +- What recovery pathways are exposed in the journey + +--- + +## Registration patterns + +### Pattern A — Inline at sign-up (low + balanced tiers) + +``` +Registration form (collect username, email) + → Account created (no password requested) + → Passkey enrollment ceremony (WebAuthn create) + Success → Account active; user lands on app + Cancel / Failed → Magic-link fallback OR password fallback (per tier) +``` + +**Trade-off:** Highest conversion-friendly path; user sees one flow. Risk: user closes the tab before completing the WebAuthn ceremony — leaves a partially provisioned account. + +**Implementation:** +- AIC: `WebAuthnRegistrationNode` — `relyingPartyName`, `userVerification: required` for higher assurance, `attachmentType: cross-platform | platform | unspecified` +- DaVinci: PingOne MFA connector → `Initiate Passkey Registration` capability; pair with HTML Form for fallback +- PingFederate: PingID adapter → FIDO2 / WebAuthn enrollment via PingID Mobile or browser + +### Pattern B — Deferred at next login (balanced + higher-assurance) + +``` +Initial sign-up → Account created with password (still required) + → Authenticated session + → Banner / interstitial: "Set up a passkey for faster sign-in" + Accept → WebAuthn create ceremony → record passkey, optionally disable password fallback per policy + Decline / Skip → Continue with password; re-prompt on Nth subsequent login +``` + +**Trade-off:** Lower drop-off at registration; opt-in adoption. Risk: many users never enroll without an incentive. + +**Tip:** Track `passkeyEnrolled` attribute on the user object; gate sensitive actions on enrollment after a soft deadline. + +### Pattern C — Opportunistic upgrade (balanced) + +``` +Existing user logs in with password + OTP + → Detect WebAuthn-capable browser/device + → Inline prompt: "Add this device as a passkey" + Accept → WebAuthn create → next login uses passkey + Decline → Continue with password; do not re-prompt for N days +``` + +**Use when:** rolling out passkeys to an existing user base; minimizes disruption. + +--- + +## Authentication patterns + +### Pattern 1 — Passkey-first with auto-fill (best UX) + +``` +Sign-in page renders WebAuthn conditional UI + → User taps username field; browser surfaces enrolled passkeys + → Single tap completes WebAuthn assertion + → Token issued +``` + +**Required:** Discoverable credentials at registration; modern browser (Safari 16+, Chrome 108+, Firefox 119+). For older browsers or fallback: explicit "Sign in with passkey" button that triggers the same ceremony non-conditionally. + +### Pattern 2 — Username-then-passkey + +``` +User enters username + → Server checks if passkey enrolled + Enrolled → WebAuthn assertion ceremony + Not enrolled → Password / OTP / magic link +``` + +**Use when:** mixed user base; some users have passkeys, some don't. + +### Pattern 3 — Magic link (passwordless without WebAuthn) + +``` +User enters email + → System sends single-use signed link + → User clicks link in email client + → Token issued +``` + +**Trade-off:** No client-side cryptography required; works on any device. Risk: phishing-resistant ONLY if the link is bound to the device that initiated the request (use `loginHint` + browser fingerprint to soft-bind). + +**Caution:** Not phishing-resistant in the strict FIDO2 sense. Acceptable for low-friction tier; not for higher assurance. + +### Pattern 4 — Passkey + step-up for sensitive actions + +``` +User signs in with passkey (low friction) + → Performs sensitive action (transfer, role change, profile update) + → Step-up evaluation + Risk LOW → Allow without re-auth + Risk MEDIUM/HIGH → Re-run WebAuthn assertion (UV: required) OR push notification +``` + +**Implementation:** +- AIC: `RiskAdvisorNode` (PingOne Protect) → `WebAuthnAuthenticationNode` with `userVerification: required` +- DaVinci: `PingOne Protect Evaluate` node → if MEDIUM/HIGH, branch to `PingOne MFA — Authenticate (Passkey)` capability + +--- + +## Recovery and fallback + +A passkey may be unavailable when the user changes phones, loses access to the credential manager, or the device is lost. Every passkey-based flow MUST expose at least one recovery path. + +| Recovery method | Assurance | Skill / node | +|---|---|---| +| Email OTP | Low | AIC `OTPCollectorDecisionNode`, DaVinci `PingOne Notifications → Send OTP` | +| SMS OTP | Low (vulnerable to SIM swap) | Same as email; configure SMS provider | +| Recovery codes (printed at enrollment) | Medium | AIC `RecoveryCodeDisplayNode` + `RecoveryCodeCollectorDecisionNode`; DaVinci subflow | +| Identity proofing (PingOne Verify) | High | `ping-universal-services` → `verify-configuration.md`; invoke from journey/flow | +| Admin-mediated reset | Highest | Out-of-band admin action; notification to user + audit trail | + +**Rule:** Match the recovery method's assurance to the friction tier. Higher-assurance tiers should NOT expose SMS OTP as the only recovery — it weakens the security posture below what passkeys provide. + +--- + +## Platform-specific implementation reference + +### PingOne ST (AIC) — Journey nodes + +| Node | Purpose | +|---|---| +| `WebAuthnRegistrationNode` | Run WebAuthn `create()` ceremony; persists credential to user object | +| `WebAuthnAuthenticationNode` | Run WebAuthn `get()` ceremony; verifies assertion | +| `WebAuthnDeviceStorageNode` | Persist device metadata (display name, last-used, AAGUID) for user-facing device list | +| `RecoveryCodeDisplayNode` | Generate and display N recovery codes at enrollment | +| `RecoveryCodeCollectorDecisionNode` | Validate a recovery code at sign-in | + +Reference: `references/curated/pingone-st/nodes/mfa-nodes.md` + +### PingOne MT (DaVinci) — Connector capabilities + +| Connector | Capability | Purpose | +|---|---|---| +| PingOne MFA | Initiate Passkey Registration | WebAuthn `create()` from DaVinci-hosted page | +| PingOne MFA | Authenticate Passkey | WebAuthn `get()` from DaVinci-hosted page | +| PingOne | Update User | Set `passkeyEnrolled: true` after successful registration | +| PingOne Notifications | Send OTP | Email OTP fallback / recovery | + +Reference: `references/curated/pingone-mt/davinci-registration-and-mfa.md` + +### Ping Software Suite — PingFederate + +| Component | Role | +|---|---| +| PingID adapter | FIDO2 / WebAuthn via PingID; configure under PingFederate authentication policies | +| Composite adapter | Chain HTML Form + PingID (with FIDO2) for password+passkey transitional flows | + +Reference: `plugins/ping-identity/skills/ping-foundation/references/curated/ping-software/pingfederate-basics.md` + +--- + +## Common gotchas + +| Gotcha | Symptom | Fix | +|---|---|---| +| `userVerification` mismatch | WebAuthn ceremony succeeds but assertion is rejected at the relying party | Match `userVerification` between registration and authentication; `required` at one end and `discouraged` at the other will fail | +| AAGUID allowlist too strict | Users with cross-platform passkeys (iCloud, Google) are rejected | Decide whether your relying party policy requires device-bound; if not, allow any AAGUID | +| Discoverable credential not requested at registration | Conditional UI shows no passkeys for the user | Set `residentKey: required` or `preferred` at `WebAuthnRegistrationNode` config | +| Browser compatibility — older Safari | Conditional UI fails silently; explicit button works | Detect support with `navigator.credentials.conditionalGet?.()` and fall back to explicit button | +| Email OTP exposed as only recovery for higher-assurance tier | Users in regulated industry can be reset via SMS-recoverable email | Require identity proofing (PingOne Verify) re-run for higher-assurance recovery | +| Magic link reused across devices | Phishable: email forwarding can leak the link | Bind link to client fingerprint or limit to single-use within short TTL | +| Passkey enrolled in wrong realm | AIC: passkey created in `bravo` realm but user signs into `alpha` | Each realm has its own RP ID; do not cross-realm enroll passkeys | + +--- + +## Prerequisites + +- Identity store user objects support a `webauthnDeviceProfile` attribute (AIC) or equivalent (PingOne MT user object includes this by default) +- Application's redirect URI domain matches the WebAuthn `rpId` configured for the relying party +- For higher-assurance tiers: PingOne Verify or another identity-proofing service licensed and configured (see `ping-universal-services`) +- For DaVinci flows: PingOne MFA service activated in the environment + +--- + +## Common variants + +| Variant | Note | +|---|---| +| Workforce + device-bound | Force `attachmentType: platform` at registration; reject roaming authenticators | +| CIAM + opt-in passkey | Pattern B (deferred enrollment); allow password fallback indefinitely | +| Hybrid passkey + OTP | Pattern 2 (username-first) with passkey OR OTP branching | +| Cross-realm | One realm per brand or workforce/CIAM split; do NOT share passkeys between realms | +| Migration from password | Pattern C (opportunistic upgrade); do not force-disable passwords until adoption is high | + +--- + +## Routing back to other skills + +| If the task is also... | Skill | +|---|---| +| Configuring the platform-level MFA policy or sign-on policy | `ping-foundation` | +| Wiring the passkey registration UI into a mobile or web app | `ping-app-integration` | +| Risk-based step-up using PingOne Protect signals | `ping-universal-services` (Protect) | +| Identity proofing as part of higher-assurance recovery | `ping-universal-services` (Verify) | + +--- + +## Related references + +- `references/curated/pingone-st/nodes/mfa-nodes.md` — WebAuthn node details +- `references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md` — passwordless registration journey +- `references/curated/pingone-st/journey-use-cases/mfa-authentication-multi-method.md` — MFA authentication patterns +- `references/curated/pingone-mt/davinci-registration-and-mfa.md` — DaVinci registration + MFA flows +- `plugins/ping-identity/skills/ping-foundation/references/curated/cross-platform/policy-and-branding-basics.md` — MFA policy configuration + +## Source + +- [PingOne MT — Passkeys overview](https://docs.pingidentity.com/pingone/authentication/p1_passkeys_overview.html) +- [PingOne ST — WebAuthn node reference](https://docs.pingidentity.com/auth-node-ref/latest/auth-node-webauthn-authentication.html) +- [DaVinci — PingOne MFA connector](https://docs.pingidentity.com/davinci/connectors/davinci_pingone_mfa_connector.html) +- [W3C WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/) +- [Passkeys.dev — RP guidance](https://passkeys.dev/docs/use-cases/) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md new file mode 100644 index 0000000..103b88e --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-flow-patterns.md @@ -0,0 +1,145 @@ +--- +title: "PingOne MT — DaVinci Flow Patterns" +product_family: pingone-mt +products: ["davinci", "pingone"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/davinci/flows/davinci_flows.html" +--- + +# PingOne MT — DaVinci Flow Patterns + +Common DaVinci flow designs for authentication, registration, MFA step-up, progressive profiling, and error handling. + +## Scope + +**Covers:** Common DaVinci flow design patterns, branching logic, error paths, and reusable subflow composition. +**Does NOT cover:** DaVinci concepts and setup — see `references/curated/pingone-mt/davinci-overview.md`. PingOne MT environment setup — see `ping-foundation`. + +--- + +## Login flow pattern + +**Minimal username/password login:** + +1. **HTTP** (start) — receives the authorization request from the application +2. **PingOne** (Read User) — look up the user by `username`; branch `Not Found` → error +3. **PingOne** (Check Password) — validate submitted password; branch `Failed` → retry or lock +4. **Flow Control** (Success) — set user token and redirect to application + +**Key decisions:** +- Use **PingOne Read User** before password check so you can branch on account status before credential validation +- Cap retries with a **Variables** counter node and branch to account lock after N failures + +--- + +## Registration flow pattern + +**Self-service registration with email verification:** + +1. **HTTP** (start) +2. **PingOne** (Read User) — check for existing account; `Found` → duplicate-account error +3. **HTML Template** — collect username, password, and profile attributes +4. **PingOne** (Create User) — create the managed object; `Failed` → error +5. **PingOne Notifications** (Send Email) — send OTP or magic link +6. **HTML Template** — collect OTP input +7. **PingOne** (Verify OTP) — validate OTP; `Failed` → retry or cancel +8. **Flow Control** (Success) — redirect + +**Key constraints:** +- On Create User failure, delete the partially created user before routing to the error path +- OTP expiry should match the HTML Template timeout; do not allow stale codes to succeed + +--- + +## MFA step-up pattern + +**Risk-triggered step-up using PingOne Protect and PingOne MFA:** + +1. Complete username/password login (see Login flow) +2. **PingOne Protect** (Evaluate) — get risk score; `LOW` → skip MFA; `MEDIUM`/`HIGH` → MFA subflow +3. **[Subflow]** MFA Authentication: + - **PingOne MFA** (Send OTP) — send to registered device + - **HTML Template** — collect OTP + - **PingOne MFA** (Verify OTP) — validate; `Failed` → retry loop; `No Device` → device registration subflow +4. **Flow Control** (Success) — set elevated token + +**Key decisions:** +- Always send the Protect result back after success AND failure to maintain the risk model +- Use a dedicated MFA subflow so it can be reused across login, registration, and step-up flows + +--- + +## Progressive profiling pattern + +**Collect additional attributes on subsequent logins:** + +1. Complete login (see Login flow) +2. **PingOne** (Read User) — check profile completeness attribute or last-prompted date +3. **Flow Control** — branch: `Complete` → skip to success; `Incomplete` → profiling +4. **HTML Template** — collect missing attributes +5. **PingOne** (Update User) — patch the managed object +6. **Flow Control** (Success) + +**Key decisions:** +- Store a `profileComplete` flag or `lastProfilePromptDate` on the user object to gate repeat prompts +- Only collect what is necessary for the current session context — avoid collecting all attributes at once + +--- + +## Error handling pattern + +Every DaVinci flow should handle three error categories: + +| Category | Cause | Handling | +|---|---|---| +| **User error** | Invalid input, wrong password, expired OTP | Re-render the HTML Template with an inline error message; do not terminate the flow | +| **System error** | Connector failure, API timeout | Log via a Variables node; redirect to a generic error page with a correlation ID | +| **Security block** | Account locked, risk threshold exceeded, fraud signal | Terminate with a clear user message; do not expose the reason beyond "account unavailable" | + +**Anti-pattern:** Terminating the flow on every error without user-visible feedback causes silent dead ends. Always ensure the user receives a visible message and a next step (retry, contact support, etc.). + +--- + +## Subflow composition + +Use subflows (Flow Connector) to share logic across multiple top-level flows: + +| Subflow | Use | +|---|---| +| MFA Authentication | Reusable MFA challenge with retry and recovery code support | +| Risk Evaluation | PingOne Protect init + eval + result reporting | +| Device Registration | Enroll a new MFA device (TOTP, push, SMS) | +| Email Verification | Send OTP, collect code, verify — used in registration and recovery | + +Map subflow output variables explicitly. All subflows should have a defined success path and a defined failure path that the parent flow handles. + +--- + +## Prerequisites + +- PingOne MT environment with DaVinci enabled +- At least one DaVinci connector configured +- PingOne application with DaVinci policy assigned (see `references/curated/pingone-mt/davinci-overview.md`) + +## Common variants + +- **Workforce flows:** use PingFederate or Okta connectors for upstream federation; MFA step-up triggers on resource access +- **CIAM flows:** progressive registration, social login with local account linking, consent collection on first login + +## Related references + +- `references/curated/pingone-mt/davinci-overview.md` + +## Source + +[DaVinci flows](https://docs.pingidentity.com/davinci/flows/davinci_flows.html) +[Getting started with flows](https://docs.pingidentity.com/davinci/flows/davinci_getting_started.html) +[DaVinci connectors](https://docs.pingidentity.com/davinci/connectors/davinci_connections.html) +[Best practices](https://docs.pingidentity.com/davinci/davinci_best_practices/davinci_best_practices.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-overview.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-overview.md new file mode 100644 index 0000000..9281976 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-overview.md @@ -0,0 +1,162 @@ +--- +title: "PingOne MT — DaVinci Overview" +product_family: pingone-mt +products: ["davinci", "pingone"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/davinci/davinci_introduction.html" +--- + +# PingOne MT — DaVinci Overview + +DaVinci is the orchestration engine for PingOne MT. It lets you build no-code/low-code authentication and identity flows using a visual flow canvas, connector library, and reusable subflows. Flows guide users through IAM activities — registration, authentication, MFA, account recovery, and self-service — and are deployed to PingOne applications via a flow policy. + +## Scope + +**Covers:** DaVinci flow model, node types, logical operators, connector model, flow variables, versioning and deployment, subflow patterns, and flow invocation methods. +**Does NOT cover:** PingOne MT environment and app setup — see `ping-foundation`. Flow design patterns — see `references/curated/pingone-mt/davinci-flow-patterns.md`. Detailed connector configuration — see per-connector references. + +--- + +## Flow model + +A DaVinci **flow** is a directed graph of **nodes** connected by **logical operators**. Every flow has: +- A **start node** (HTTP connector or trigger) that receives the initial request +- **Nodes** — each performs exactly one task via a connector capability +- **Logical operators** — sit between nodes, route execution based on outcomes +- One or more **success paths** that redirect the user to the application +- One or more **failure paths** that return an error + +**Node visual types:** +| Border | Meaning | +|---|---| +| Dotted | Involves user interaction (forms, displays) | +| Solid | Runs silently in the background | + +--- + +## Logical operators + +| Operator | Behavior | +|---|---| +| If All True | All preceding nodes succeeded | +| If Any True | At least one node succeeded | +| If All False | All preceding nodes failed | +| If Any False | At least one node failed | +| All Triggers Complete | All nodes finished (success or failure) | +| Any Trigger Completes | Any node finished (success or failure) | + +Use **If All True** for sequential happy-path steps. Use **If Any True** when any one of several parallel checks is sufficient to proceed. + +--- + +## Connector model + +Connectors integrate DaVinci with Ping products or external services. Each connector exposes discrete **capabilities** (specific actions). + +| Category | Examples | +|---|---| +| Core | Flow Control, Variables, Functions — no external config needed | +| Identity | PingOne (user CRUD, MFA, Verify), PingOne Notifications | +| Authentication | PingFederate, Okta, Google, Apple | +| Risk | PingOne Protect, SEON | +| Notification | Twilio, Mailchimp | +| API | HTTP, Generic Connector (arbitrary REST calls) | + +**Connector instances:** The same connector can be added multiple times with different credentials — use one instance for dev, another for production. Configuration changes to an instance propagate to every flow node using that instance automatically. + +**Credential storage:** Credentials are stored at the connector instance level, not per node. Enter them once; all flow nodes sharing the instance use the same credentials. + +--- + +## Flow variables + +| Scope | Lifetime | Use | +|---|---|---| +| Flow variables | Single flow execution | Carry data between nodes within one flow invocation | +| Company variables | Persistent across flows | Configuration constants, feature flags, global counters; shared across all flows in the environment | + +Variable types: `string`, `number`, `boolean`, `object`, `list`, `secret`. + +**Key constraint:** Variables must be declared before they can be read. Reading an undeclared variable produces a runtime error that silently skips the node in some cases — always declare explicitly. + +**UI Studio:** A companion tool for customizing the HTML/CSS of DaVinci-hosted pages (hosted by DaVinci, not your application). Use it to apply branding without writing raw HTML. + +--- + +## Versioning and deployment + +| Action | Effect | +|---|---| +| **Save** | Creates a new version entry; does not affect the live (deployed) flow | +| **Deploy** | Publishes the current saved state as the live version | +| **Try Flow** | Test run using the deployed version; grayed out if flow has never been deployed | +| **Flow Versions → Revert** | Roll back to any prior version | + +**Draft vs. deployed:** Changes are only visible to users after Deploy. Save frequently to preserve version history. Revert without re-deploying to roll back. + +**Debug logging:** Enable via More options → Flow Settings → Logging → Debug. Required for diagnosing node-level failures and unexpected branching. + +--- + +## Subflow patterns + +A **subflow** is a DaVinci flow called from within another flow using the **Flow Connector**. Use subflows to: +- Share logic across multiple flows (e.g., MFA step-up, risk evaluation, email verification) +- Keep individual flows focused and below a manageable node count +- Version and test shared components independently + +**Subflow output:** The called flow returns its output variables to the parent flow. Map output variable names explicitly in the Flow Connector node. All subflows must define both a success path and a failure path that the parent flow handles. + +--- + +## Flow invocation methods + +A DaVinci flow is exposed to users via one of three methods: + +| Method | How it works | Best for | +|---|---|---| +| **Redirect** | Full-page redirect to DaVinci-hosted URL; OIDC/SAML authentication via PingOne policy | Flows with UI; fastest to deploy; no custom HTML required | +| **Widget** | Flow embedded in the application page; stays on same URL | When UX must remain on the app's own domain; requires minimal app-side code | +| **API** | Flow invoked via direct API call | Flows without direct user interaction (M2M, backend orchestration) | + +**Redirect integration steps:** +1. Build the flow in DaVinci and Deploy it. +2. Create a DaVinci Application in DaVinci; assign the flow and a flow policy. +3. In PingOne, assign the DaVinci flow policy to the PingOne application's sign-on policy. +4. PingOne OIDC/SAML login now triggers the DaVinci flow. + +**A/B testing:** DaVinci applications support splitting traffic across flow versions or different flows for controlled rollouts. + +--- + +## Prerequisites + +- PingOne MT environment with DaVinci service activated +- Admin access to DaVinci console (`davinci.pingidentity.com`) +- At least one PingOne connector instance configured for user operations + +## Common variants + +- **Workforce flows:** triggered by a PingOne SAML or OIDC application; use PingFederate or Okta connectors for upstream federation; MFA step-up on resource access +- **CIAM flows:** triggered from a web/mobile app; progressive registration via redirect; PingOne MFA + PingOne Verify for onboarding +- **Template-based:** download a pre-built flow JSON from the Ping Identity Marketplace, import it via Add Flow → Import From JSON, then customize + +## Related references + +- `references/curated/pingone-mt/davinci-flow-patterns.md` + +## Source + +[DaVinci introduction](https://docs.pingidentity.com/davinci/davinci_introduction.html) +[DaVinci flows](https://docs.pingidentity.com/davinci/flows/davinci_flows.html) +[Getting started with flows](https://docs.pingidentity.com/davinci/flows/davinci_getting_started.html) +[Connectors](https://docs.pingidentity.com/davinci/connectors/davinci_connections.html) +[Implementing a flow in an application](https://docs.pingidentity.com/davinci/integrating_flows_into_applications/davinci_how_to_implement_a_flow.html) +[Best practices](https://docs.pingidentity.com/davinci/davinci_best_practices/davinci_best_practices.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-registration-and-mfa.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-registration-and-mfa.md new file mode 100644 index 0000000..ba7b323 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-mt/davinci-registration-and-mfa.md @@ -0,0 +1,191 @@ +--- +title: "PingOne MT — DaVinci Registration and MFA Flow Patterns" +product_family: pingone-mt +products: ["davinci", "pingone"] +capabilities: ["orchestration"] +services: ["mfa"] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/davinci/flows/davinci_getting_started.html" +--- + +# PingOne MT — DaVinci Registration and MFA Flow Patterns + +Complete flow designs for user registration with email verification, and MFA step-up using PingOne MFA and PingOne Protect — the two most common DaVinci use cases. + +## Scope + +**Covers:** Self-service registration with email OTP verification, MFA device enrollment, risk-triggered step-up, subflow composition, and error path patterns for both scenarios. +**Does NOT cover:** DaVinci concepts and setup — see `references/curated/pingone-mt/davinci-overview.md`. Generic flow patterns — see `references/curated/pingone-mt/davinci-flow-patterns.md`. PingOne app and environment setup — see `ping-foundation`. + +--- + +## Registration with email verification + +**Goal:** Create a PingOne user, send a verification OTP, and gate progression on OTP confirmation. + +### Flow structure + +``` +[HTTP — Start] + → PingOne (Read User by username) + If Found → [Error: Account already exists] + If Not Found → + → HTML Form (collect: username, password, given name, family name, email) + → PingOne (Create User) + If Failed → [Delete partial user if created] → [Error] + If Success → + → PingOne Notifications (Send OTP to email) + → HTML Form (collect OTP input) + → PingOne (Verify OTP) + If False → Retry loop (max 3) → [Error: Too many attempts] + If True → + → PingOne (Update User — set emailVerified: true) + → [Success — redirect to application] +``` + +### Key decisions and constraints + +| Decision point | Rule | +|---|---| +| Check for existing user before Create | Prevents duplicate account errors from the Create User node; surfaced early with a clean error message | +| Delete partial user on Create failure | If Create User fails mid-flow, delete the partial record before routing to error to prevent orphaned accounts | +| OTP expiry alignment | Set OTP validity in PingOne Notifications to match the HTML Form session timeout; expired OTPs cause false `Invalid code` errors | +| Retry cap | Track attempt count with a Company Variable or Functions connector; redirect to error after 3 failures to prevent OTP brute force | +| Post-verification attribute | Set `emailVerified: true` on the user object via Update User after OTP success; downstream flows can gate on this attribute | + +### Subflow recommendation + +Extract the OTP verification steps (Send → Collect → Verify) into a reusable `Email Verification` subflow. It can be called from registration, recovery, and email-change flows without duplication. + +--- + +## MFA enrollment at registration + +**Goal:** Require users to enroll a second factor during or immediately after registration. + +### Inline enrollment (end of registration flow) + +``` +[After PingOne Create User succeeds] + → PingOne MFA (Get Devices) + If no devices → [MFA Enrollment subflow] + If devices exist → [skip enrollment, proceed to success] + → [Success] +``` + +**MFA Enrollment subflow:** +``` +[HTML Form — method selection: TOTP / SMS / Email OTP] + → PingOne MFA (Send Pairing Key or initiate enrollment) + → HTML Form (collect enrollment confirmation) + → PingOne MFA (Verify enrollment) + If Failed → Retry → [Error after max retries] + If Success → + → [Return to parent — enrolled] +``` + +### Deferred enrollment (on next login) + +``` +[Login flow — after credential verification] + → PingOne MFA (Get Devices) + If no devices → [MFA Enrollment subflow] (above) + If devices exist → [MFA Authentication subflow] + → [Success] +``` + +**Use deferred enrollment** when MFA adoption is being rolled out gradually or when the registration flow is already complex. + +--- + +## MFA step-up with PingOne Protect + +**Goal:** Evaluate risk at sign-in and require MFA only when risk is elevated. + +### Flow structure + +``` +[HTTP — Start] + → PingOne Protect (Initialize) ← always first; sets up the risk session + → HTML Form (collect username + password) + → PingOne (Read User + Check Password) + If Failed → PingOne Protect (Update — failed auth) → [Error] + If Success → + → PingOne Protect (Evaluate) + LOW risk → PingOne Protect (Update — success, no MFA) → [Success] + MEDIUM risk → [MFA subflow] + HIGH risk → [MFA subflow] or [Block] + → PingOne Protect (Update — success, with MFA) + → [Success] +``` + +### PingOne Protect initialization rules + +| Rule | Reason | +|---|---| +| Initialize before credential collection | The risk session must be open before any user signals are captured | +| Always call Update after success or failure | Risk model degrades if result is never reported back; the Update call closes the risk session | +| Use HIGH risk → block for admin or privileged accounts | High-risk admin sessions should not be salvageable with MFA alone | + +### MFA subflow (reusable) + +``` +[PingOne MFA — Get Devices] + If no devices → [MFA Enrollment subflow] + If devices → [Choose method or use default] + → PingOne MFA (Send OTP / initiate push) + → HTML Form (collect OTP or poll push result) + → PingOne MFA (Verify) + If Failed → Retry loop (max 3) → [Block] + If Success → [Return to parent — MFA passed] +``` + +Extract this into a `MFA Authentication` subflow. Reference it from both login and step-up flows to ensure consistent behavior. + +--- + +## Error handling + +| Category | DaVinci pattern | +|---|---| +| User input error (wrong password, invalid OTP) | Re-render HTML Form with inline error message via a Variables node; set error message in flow variable before looping back | +| System error (connector failure, API timeout) | Log with Functions connector; redirect to generic error page with a correlation ID stored in a flow variable | +| Security block (account locked, risk HIGH + no MFA) | Terminate via Flow Control with a user-visible message; do not expose the specific reason | +| Partial state cleanup | On Create User failure or mid-enrollment abort, explicitly call Delete User or cleanup node before routing to error | + +**Anti-pattern:** Routing every error outcome to a blank dead-end (no message, no next step). Always set a user-visible error message before terminating. + +--- + +## Prerequisites + +- PingOne MT environment with DaVinci and PingOne MFA services activated +- PingOne connector instance configured with environment-appropriate credentials +- PingOne Notifications (email) configured and sender domain verified +- For risk step-up: PingOne Protect service activated and PingOne Protect connector instance configured + +## Common variants + +| Variant | Pattern | +|---|---| +| Social login + local registration | SelectIdP node routes social users to PingOne Social Provider connector; new users auto-provisioned via Create User | +| Progressive profiling after login | After login success, check profile completeness attribute; route incomplete users to attribute collection HTML Form | +| Workforce MFA with Push | Replace TOTP enrollment with PingOne MFA Push; same subflow structure; requires PingID Mobile app | +| Delegated admin enrollment | Admin creates user via PingOne API, sets `mustChangePassword: true`; first login triggers MFA enrollment before password change | + +## Related references + +- `references/curated/pingone-mt/davinci-overview.md` — DaVinci concepts, versioning, invocation methods +- `references/curated/pingone-mt/davinci-flow-patterns.md` — generic flow patterns (login, registration, step-up, error handling) + +## Source + +[Getting started with DaVinci flows](https://docs.pingidentity.com/davinci/flows/davinci_getting_started.html) +[DaVinci best practices](https://docs.pingidentity.com/davinci/davinci_best_practices/davinci_best_practices.html) +[DaVinci subflows](https://docs.pingidentity.com/davinci/davinci_best_practices/davinci_best_practices_subflows.html) +[Implementing a flow in an application](https://docs.pingidentity.com/davinci/integrating_flows_into_applications/davinci_how_to_implement_a_flow.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md new file mode 100644 index 0000000..8cfb170 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-design-patterns.md @@ -0,0 +1,306 @@ +--- +title: "AIC and PingAM — Journey Design Reference" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect", "admin"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html" +--- + +# AIC and PingAM — Journey Design Reference + +How to design, review, and reason about AIC and PingAM journeys in a way that is useful to architects, developers, support teams, and product teams. Applies to sign-in, MFA, registration, passwordless, recovery, profile management, delegated administration, and cross-channel identity flows. + +## Scope + +**Covers:** Journey design principles, lifecycle modeling, risk and fallback patterns, session behavior, messaging, supportability, and product-specific notes for AIC and PingAM. +**Does NOT cover:** Individual node configuration — see `nodes/`. DaVinci flow design — see `../pingone-mt/davinci-overview.md`. Platform setup — see `ping-foundation`. + +--- + +## Before designing: seven questions to answer first + +Before suggesting any node or implementation detail, an agent should be able to answer: + +1. Who is the user or actor? +2. What state are they in now? +3. What does success mean? +4. What should happen if risk increases? +5. What should happen if a dependency fails? +6. What changes in session or assurance posture after success or failure? +7. What will support need to reconstruct the experience later? + +If any of these are undefined, the journey is not ready to build. + +--- + +## Core design principles + +### Start with the user and lifecycle state, not the node list + +Define actor types first: customer, employee, admin, delegated admin, partner admin, or machine actor. + +Define lifecycle states: invited, pending verification, active, suspended, disabled, locked, closed. + +Define the attributes that matter: username, email, phone, tenant, locale, MFA state, recovery identifiers, consent state, role, group, entitlement state. + +Keep these meanings consistent across login, registration, recovery, and step-up flows. If one app interprets the same user state differently from another, the experience will drift and support will become harder. + +### Keep the journey explicit and explainable + +A journey diagram should show the happy path, risk branches, fallback branches, degraded-mode behavior, and user-facing outcomes. + +Every branch should answer: what happened, what the user sees, what they can do next, and whether support or an admin is needed. + +Avoid hidden behavior that only exists in scripts or policy conditions with no user-visible explanation. + +### Match friction to risk + +Use a small, understandable set of risk outcomes: allow, light check, step-up, limited fallback, or safe deny. + +Do not challenge every user the same way. Do not under-protect privileged or high-impact actions because the main flow was optimized for speed. + +The right question is not "can we add more authentication?" but "when is additional friction justified, for whom, and for which action?" + +### Design for clear fallback behavior + +Journeys should define what happens when: +- A risk engine is unavailable +- An email or SMS provider is delayed +- A factor is unavailable +- A link is expired or a token is stale +- An upstream IdP is slow +- A required claim is missing + +Avoid loops, blank screens, silent retries, and raw technical errors. If the preferred path is unavailable, the user should get either a safe alternate route or a clear explanation of why the flow cannot continue. + +### Keep session behavior understandable + +Decide explicitly what happens after: sign-in, password change, privilege elevation, MFA reset, device deregistration, logout, and entitlement changes. + +Be explicit about idle timeout, max lifetime, renewal, forced re-authentication, and revocation behavior. + +If a session posture changes, the experience should make that visible instead of forcing support to infer it from logs. + +### Keep messaging safe, human-readable, localized, and accessible + +User-facing text should explain the next step without exposing raw policy outcomes, protocol details, stack traces, or account existence. + +This matters especially for login, recovery, step-up, and degraded-mode messaging. Accessibility and localization are not polish items — they are part of the journey definition. + +### Design for change and operations + +Important journeys should be observable, versioned, tested, and safe to roll forward or back. + +Identity journeys are products, not one-time diagrams. They need release discipline, telemetry, support readiness, and rollback plans. + +--- + +## Good default journey shape + +### Step 1 — Define persona, channel, trust level, and success criteria + +- Is this SIAM, workforce, B2B, or delegated admin? +- Does the flow start in hosted UI, an SDK, a native app, a browser redirect, a webview, or an external IdP? +- What outcome should the user achieve, in business terms? + +### Step 2 — Model the happy path first + +Start with the shortest correct flow for the intended user. Confirm which systems are read from, which are written to, and what timing assumptions exist. Keep early branching to a minimum until the intended baseline experience is clear. + +### Step 3 — Add risk and policy branches + +- What causes extra friction? +- What causes a hard block versus limited fallback? +- Where do privileged flows differ from normal ones? + +### Step 4 — Add fallback and degraded-mode behavior + +- What happens if signals, factors, providers, or claims are unavailable? +- Include retry guidance, alternate channels, assisted options, and stop conditions. +- Explicitly prevent redirect loops and repeated returns to unusable screens. + +### Step 5 — Add safe messaging and state transitions + +Use non-technical, localized, accessible messaging. Make recovery, consent, timeout, step-up, and error states understandable. Preserve anti-enumeration requirements wherever account existence or user status could leak. + +### Step 6 — Add telemetry and supportability + +Include correlation IDs or equivalent reference codes where the user experience or support workflow needs them. Capture journey version, major branch decisions, failure categories, and abandonment points. Make sure support can reconstruct what the user saw. + +### Step 7 — Version and release conservatively + +Clone or version before major customization. Roll out high-impact identity changes in controlled stages. Know how to roll back without breaking users who are mid-journey. + +--- + +## What "good" looks like by journey type + +### Sign-in and MFA + +A good sign-in journey is short for low-risk users, stronger for high-risk situations, and predictable across channels. + +- Use contextual risk to decide when to add friction instead of applying the same challenge to every user and channel. +- Keep prompts and outcomes consistent across hosted, SDK, mobile, embedded, and custom UI. +- Prefer phishing-resistant and lower-friction approaches where appropriate (passkeys, passwordless). +- Define what happens when the preferred authenticator is unavailable. +- Define what happens when the user changes device, location, browser, privilege level, or assurance level. +- Make session upgrades, step-up prompts, and token posture changes visible in the UX. + +**Common failure patterns:** too much friction for low-risk users; too little for privileged users; inconsistent MFA between mobile and web; no alternate factor when the preferred factor fails; unexplained step-up prompts. + +### Registration and onboarding + +A good registration journey aligns to the identity model and to downstream system expectations. + +- Align form fields and validation with the identity model so registration does not drift from profile, recovery, and entitlement logic. +- Define which attributes are required, optional, conditionally required, or externally mastered. +- Decide where verification happens: email, phone, MFA setup, admin approval, or identity verification. +- Show users what is happening when provisioning or synchronization takes time. +- Test under realistic load including email verification, MFA setup, and cross-device handoffs. +- Include edge cases: duplicate emails, existing accounts, partially provisioned users, disabled identities, conflicting invites. + +**Common failure patterns:** attributes that do not match downstream requirements; no clear status when provisioning lags; assuming single-device flows; weak handling of duplicate or partially created accounts. + +### Recovery and self-service + +Recovery is one of the highest-risk journey families and should be treated as such. + +- Treat recovery as a high-risk journey. Add stronger verification, step-up authentication, or identity verification where justified. +- Use non-revealing messages ("If an account exists, we've sent instructions") paired with rate limits, lockouts, and clear next steps. +- Recovery artifacts (links, codes, tokens) should be short-lived, one-time use, and invalidated after use or suspected compromise. +- Define distinct handling for: password reset, username reminder, MFA reset, device recovery, email/phone change, and assisted recovery. +- Define what happens when limits trigger, when abuse is suspected, and when assisted recovery is required. + +**Common failure patterns:** reusable or long-lived recovery artifacts; messages with no next step; strong MFA in sign-in but weak verification in recovery; delivery-channel failures with no alternate route. + +### Passwordless and step-up evolution + +The long-term direction should be lower friction with stronger assurance. + +- Reduce dependence on passwords where possible. +- Prefer strong authenticators over memorized secrets when the use case supports it. +- Use step-up based on risk and action sensitivity rather than applying it everywhere by default. +- Roll out passwordless changes incrementally — only some users may be ready. +- Make sure fallback and recovery are mature before aggressively reducing password-based options. + +**Common failure patterns:** assuming all populations are ready for the same passwordless posture; removing fallback too early; excessive step-up fatigue; changing friction without explaining why. + +### Profile, entitlement, and delegated flows + +These flows are often treated as secondary, but they are where state drift becomes visible. + +- Treat profile updates, device changes, consent changes, role changes, and delegated admin actions as identity journeys. +- Make state changes visible and predictable. +- Define how session and access should change after profile, role, or device updates. +- In B2B and delegated admin journeys, make privilege scope obvious and separate from end-user views. +- Show admins what they can do, cannot do, and the current state of the tenant or user. + +**Common failure patterns:** role changes that do not affect active sessions as expected; delegated admin actions with weak scope cues; missing auditability for high-impact actions. + +--- + +## Common watch-outs + +- Do not let different apps interpret the same user state, role, or attribute differently — that creates drift across login, recovery, delegated, and profile flows. +- Do not leak account existence or internal conditions through recovery, sign-in, step-up, or error messages. +- Do not assume web-only behavior. Cross-channel transitions, deep links, app switching, webviews, and native flows need explicit design and testing. +- Do not rely on undefined degraded behavior. Risk engine outages, directory lag, email/SMS delays, claim gaps, and IdP failures should have intentional fallback UX. +- Do not let session and privilege changes be invisible. Regenerate or invalidate sessions after sign-in, password change, privilege change, or device change, and make the effect clear. +- Do not customize OOTB flows without a rollback path or clone/version strategy. +- Do not ship journey changes without telemetry, version awareness, and support readiness. +- Do not treat accessibility, localization, or cross-device behavior as optional. + +--- + +## Product-specific notes + +### AIC + +AIC guidance should be read with a tenant-security and operational lens. + +- AIC security guidance stresses HTTPS-only usage, trusted cookie-domain configuration, CORS controls, CSRF protections for `/am/json/` endpoints, and audit logging as part of secure tenant design. +- For account recovery, AIC guidance recommends step-up authentication, risk-based signals (PingOne Protect), stronger identity verification where needed, verified and unique recovery identifiers, and regular review of recovery processes. + +When reasoning about AIC journeys, pay attention to: +- How hosted pages, cookies, and APIs interact across domains +- How tenant security controls affect experience design +- How recovery identifiers are verified and protected +- How risk signals affect sign-in and recovery consistency +- How auditability and support tracing are preserved + +### PingAM + +PingAM guidance should be read with a realm, redirect, and session-governance lens. + +- Reserve the root realm for administrative operations; use another realm (e.g., `alpha`) for journey work. +- Enable only the `goto`/redirect targets you actually trust after journey completion — PingAM denies them by default until the validation service is configured. +- Be intentional with session lifetime settings and client-side sessions, because session behavior is a core part of the security outcome. + +When reasoning about PingAM journeys, pay attention to: +- Realm boundaries and administrative separation +- Redirect and return URL trust boundaries +- Session lifetime and idle timeout posture +- How the journey hands control back to the relying application +- How privilege and assurance changes affect active sessions + +--- + +## Recommended agent behavior when using this reference + +**Speak in terms of journey intent, risk, fallback, session impact, state transitions, and user-visible behavior — not just nodes and plumbing.** + +When describing or proposing a journey, include: +- Who the user is +- What state they begin in +- What success means +- What the main path is +- Where risk or policy branches appear +- What fallback exists if dependencies fail +- What changes in session, token, or assurance posture occur +- What the user sees at each important branch +- What telemetry or traceability is needed + +When recommending changes, call out: +- Security impact +- User-experience impact +- Supportability impact +- Rollout and rollback considerations +- Cross-channel implications + +If the request is implementation-specific, follow up with environment-specific material before prescribing exact nodes, scripts, policy settings, redirect patterns, or session settings. + +If the request is high level, stay at the level of design patterns, user-visible behavior, operational watch-outs, and journey structure. + +**Prefer guidance that is safe, explainable, supportable, and testable over guidance that is merely technically possible.** + +--- + +## Prerequisites + +- PingOne AIC or PingAM tenant with at least one realm and journey capability enabled. + +## Common variants + +- **CIAM design defaults:** anti-enumeration required on all recovery/reset flows; progressive consent collection; social login with local account fallback. +- **Workforce design defaults:** step-up authentication tied to resource sensitivity; delegated admin separation; stricter session lifetime enforcement. + +## Related references + +- `nodes/basic-auth-nodes.md` +- `nodes/mfa-nodes.md` +- `nodes/risk-management-nodes.md` +- `nodes/identity-management-nodes.md` +- `nodes/utility-nodes.md` +- `nodes/federation-contextual-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) +[Authentication node reference](https://docs.pingidentity.com/auth-node-ref/latest/overview.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/account-recovery-and-username-reminder.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/account-recovery-and-username-reminder.md new file mode 100644 index 0000000..6aa67a1 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/account-recovery-and-username-reminder.md @@ -0,0 +1,138 @@ +--- +title: "PingOne ST — Account Recovery and Username Reminder" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Account Recovery and Username Reminder + +Design patterns for the OOTB account recovery and username reminder journey family, derived from live AIC journey exports. + +## Scope + +**Covers:** Anti-enumeration wiring, EmailSuspendNode resume URI pattern, DisplayUserNameNode, in-browser vs. email-body username delivery, email template variables. +**Does NOT cover:** Password reset or password update — see `journey-use-cases/password-reset-and-update.md`. MFA reset — compose with `nodes/mfa-nodes.md`. + +--- + +## Account Recovery (Username Recovery) + +The OOTB Account Recovery journey recovers a **username** (not a password) by looking up the account by email address and displaying the username in the browser after email verification. + +**Node sequence:** +``` +PageNode (collect mail) + → IdentifyExistingUserNode (identityAttribute: mail, identifier: userName) + → true AND false → EmailSuspendNode (anti-enumeration) + → DisplayUserNameNode (shows recovered username in browser) + → InnerTreeEvaluatorNode (tree: Login) + → true → SuccessNode + → false → FailureNode +``` + +**Email template:** `emailVerification` — contains `{{object.resumeURI}}` as the resume link. 24-hour expiry. English only in OOTB. + +**Key decision:** Username is shown in the browser after email verification (via `DisplayUserNameNode`). The user then proceeds directly to Login. This is suitable when you want the user to see their username and immediately authenticate. + +--- + +## Forgotten Username (Email Delivery) + +The OOTB Forgotten Username journey sends the username inside the email body rather than displaying it in the browser. + +**Node sequence:** +``` +PageNode (collect mail, validateInputs: false) + → IdentifyExistingUserNode (identityAttribute: mail, identifier: userName) + → true AND false → EmailSuspendNode (anti-enumeration) + → InnerTreeEvaluatorNode (tree: Login) + → true → SuccessNode + → false → FailureNode +``` + +**Email template:** `forgottenUsername` — contains `{{object.userName}}` inline in the email body; also provides `{{object.resumeURI}}` as a login link. Bilingual (English + French) in OOTB — the only bilingual template in the standard set. + +**Key decision:** No `DisplayUserNameNode` — the username never appears in the browser. The user receives it by email and then follows the resume link to the Login journey. + +--- + +## Anti-Enumeration Pattern + +**Critical:** Both `true` and `false` outcomes from `IdentifyExistingUserNode` must connect to the **same** `EmailSuspendNode`. The user-facing message must be identical regardless of whether an account was found. + +``` +IdentifyExistingUserNode + → true ─┐ + ├─→ EmailSuspendNode ("If the details provided match our records...") + → false ─┘ +``` + +**User-facing message (OOTB):** `"If the details provided match our records, you will receive an email with further instructions."` + +**Never route `true` and `false` to different messages** — that leaks account existence and violates the OWASP recommendation against username enumeration. + +**Rate limiting:** The anti-enumeration message alone does not prevent abuse. Add rate limiting at the WAF or API gateway level for production deployments. + +--- + +## EmailSuspendNode Configuration + +| Field | Value | Notes | +|---|---|---| +| `emailTemplateName` | `emailVerification` or `forgottenUsername` | Template must contain `{{object.resumeURI}}` | +| `objectLookup` | `true` | Required for the template to access managed object attributes | +| Message | User-facing browser message | Shown while the user waits for the email | + +**Resume URI behavior:** When the user clicks the link in the email, the journey resumes at the node immediately after `EmailSuspendNode`. The resume URI is single-use and time-limited (24 hours in OOTB templates). + +--- + +## Email Template Variables + +| Variable | Available in | Notes | +|---|---|---| +| `{{object.resumeURI}}` | Both templates | Journey resume link; required for email-gated flows | +| `{{object.userName}}` | `forgottenUsername` | Embeds the username in the email body; use `{{#if object.userName}}` conditional | +| `{{object.mail}}` | Available | User's email address | + +--- + +## DisplayUserNameNode vs. email-body delivery + +| Approach | When to use | +|---|---| +| `DisplayUserNameNode` (Account Recovery) | User should see their username in the browser and immediately log in; in-person or single-device flow | +| Email body delivery (Forgotten Username) | Username delivered out-of-band; user may be on a different device when reading the email | + +--- + +## Inner Journey Handoff + +Both OOTB journeys end with `InnerTreeEvaluatorNode(tree: Login)` — the user is routed into the Login journey after recovery. This avoids duplicating authentication logic in the recovery journey. + +**Configuration:** The Login inner journey must exist and be activated in the same realm. + +## Prerequisites + +- Email notification service configured in the tenant +- Email templates (`emailVerification`, `forgottenUsername`) present in IDM notification templates +- `managed/alpha_user` with `mail` and `userName` attributes accessible + +## Related references + +- `journey-use-cases/password-reset-and-update.md` +- `nodes/identity-management-nodes.md` +- `nodes/utility-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/financial-services-step-up.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/financial-services-step-up.md new file mode 100644 index 0000000..f46327b --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/financial-services-step-up.md @@ -0,0 +1,194 @@ +--- +title: "PingOne ST — Financial Services Step-Up and Transaction Authorization" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: ["protect"] +audience: ["developer", "architect"] +use_cases: ["customer", "workforce"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Financial Services Step-Up and Transaction Authorization + +Design patterns for financial-grade authentication journeys: full lifecycle credential outcomes, PingOne Authorize for per-transaction policy, step-up MFA, KBA, and operational routing. Derived from the Financial Services and Money Transfer journey exports. + +## Scope + +**Covers:** `IdentityStoreDecisionNode` lifecycle outcomes, `PingOneAuthorizeNode` transaction authorization, PUSH_REQ/APPROVAL_REQ/KBA_REQUIRED action responses, Enhanced KBA, `ConfigProviderNode`, `SetSuccessUrlNode`, `PollingWaitNode`. +**Does NOT cover:** PingOne Protect risk integration — see `journey-use-cases/pingone-protect-risk-integration.md`. MFA device registration — see `journey-use-cases/passwordless-mfa-registration.md`. + +--- + +## Main Journey Structure + +``` +ScriptedDecisionNode ("Prerequisites & Init Variables") + → ScriptedDecisionNode ("Is Protect Analysis Required?") + → InnerTreeEvaluatorNode (Initialize P1 Protect) + → InnerTreeEvaluatorNode (Protect Evaluation & Mitigation) + → InnerTreeEvaluatorNode (SignOn) + → AuthLevelDecisionNode ("MFA Based On Risk Level?") + → true → InnerTreeEvaluatorNode (MFA Authentication) + → false → continue + → InnerTreeEvaluatorNode (Manage Account) + → SetSuccessUrlNode ("Redirect to Manage Account Page") + → PollingWaitNode +``` + +The `AuthLevelDecisionNode` after SignOn checks whether risk evaluation elevated the auth level — only triggers MFA if needed. This is the risk-driven step-up pattern. + +--- + +## IdentityStoreDecisionNode — Full Lifecycle Outcomes + +Used in the SignOn inner journey instead of `DataStoreDecisionNode` to handle account lifecycle states explicitly. + +- Outcomes: **TRUE** / **FALSE** / **LOCKED** / **EXPIRED** / **CANCELLED** + +**Required wiring per outcome:** + +| Outcome | Recommended routing | +|---|---| +| `TRUE` | Continue journey (authentication success) | +| `FALSE` | Retry limit → FailureNode | +| `LOCKED` | Message node ("Account locked") → account recovery flow or FailureNode | +| `EXPIRED` | Password change inner journey → re-authenticate | +| `CANCELLED` | FailureNode (user cancelled the authentication) | + +Never leave `LOCKED` or `EXPIRED` routing to a generic FailureNode in production — users cannot self-service their way out. + +--- + +## Transaction Authorization: PingOneAuthorizeNode + +Used to enforce fine-grained policy on individual transactions. Evaluates an authorization policy in PingOne Authorize, distinct from authentication. + +**Standard payment/transfer pattern:** +``` +PageNode (collect transaction details) + → PingOneAuthorizeNode + → permit → PatchObjectNode (update balance/record) → SuccessNode + → deny → message or FailureNode + → PUSH_REQ → ScriptedDecisionNode ("Push Approval Notification") + → continue / error / noMail → PageNode ("Transfer Success") + → APPROVAL_REQ → EmailSuspendNode ("Transfer Approval via Email") + → [user approves by clicking link] + → PatchObjectNode → SuccessNode + → indeterminate → FailureNode + → clientError → FailureNode +``` + +**`indeterminate` and `clientError` must have explicit handlers.** Do not leave them unwired — an indeterminate authorization should fail safe (deny), not silently succeed. + +**KBA-based authorization (Enhanced KBA):** +``` +PingOneAuthorizeNode + → KBA_REQUIRED → ScriptedDecisionNode ("Calculate KBA Threshold") + → PageNode ("Display Questions") + outcomes: error / limitExceeded / noQuestions / questions + → questions: PageNode (KBA answers) + → SuccessNode / FailureNode + → permit → proceed without KBA +``` + +`PingOneAuthorizeNode` determines *whether* KBA is required via policy — the journey then presents the appropriate questions. + +--- + +## ConfigProviderNode for Externalized Messaging + +Used to decouple user-facing message text from journey scripting. + +- Outcomes: **outcome** / **CONFIGURATION_FAILED** +- Reads from ESV (environment-level secret/variable) or configuration store +- `CONFIGURATION_FAILED` must be handled — use a fallback hardcoded message rather than routing to FailureNode + +**Pattern:** +``` +ConfigProviderNode ("Set Success Message") + → outcome → [next node uses successMessage from shared state] + → CONFIGURATION_FAILED → [proceed with fallback message] +``` + +--- + +## SetSuccessUrlNode — Post-Journey Redirect + +Used to redirect the user to an application page after the journey completes, rather than the default OIDC redirect. + +- Single `outcome` +- Place after the last journey step, before `PollingWaitNode` if async provisioning is needed + +**Observed use:** Financial Services and Money Transfer redirect to the application's account management or transfer page after session establishment. + +--- + +## PollingWaitNode at Journey End + +Used to wait for asynchronous provisioning or account operations to complete before declaring success. + +- `secondsToWait: 5` +- Outcomes: **DONE** / **EXITED** +- Observed at the end of: registration journeys (waiting for IDM to complete provisioning), financial services main journey (waiting for session enrichment) +- `EXITED`: route to SuccessNode — user has dismissed the wait screen; do not block on polling + +--- + +## T&C Enforcement in Authenticated Journeys + +Authentication journeys in financial-grade use cases enforce current T&C version on each login: + +``` +TermsAndConditionsDecisionNode + → false (not accepted or outdated) → AcceptTermsAndConditionsNode → continue + → true (accepted and current) → continue +``` + +This ensures users who have not accepted a new T&C version are prompted on the next sign-in, not just at registration. + +--- + +## Email Verification Gate in SignOn + +``` +AttributeValueDecisionNode ("Is Email Verified?") + → false → [email verification inner journey] + → [verified] → continue + → true → continue +``` + +Users who registered without completing email verification are gated on each subsequent login until they verify. + +--- + +## Security Considerations + +- `IdentityStoreDecisionNode(LOCKED)` must route to a meaningful UX — do not silently fail locked accounts +- `PingOneAuthorizeNode(indeterminate)` must fail safe — deny by default +- High-risk Protect outcomes should disable the account and send an admin notification before routing to Failure +- All `PingOneProtectResultNode` calls must appear at both success and failure paths +- Session should be re-evaluated or re-issued after a privilege change or step-up MFA event + +## Prerequisites + +- PingOne Protect service enabled (for risk integration) +- PingOne Authorize service enabled with transaction policies configured (for PingOneAuthorizeNode) +- Twilio Verify credentials configured (for SMS/VOICE MFA paths) +- Email notification templates: `disabledAccountRecovery`, `magicLinkTemplate`, transaction approval templates + +## Related references + +- `journey-use-cases/pingone-protect-risk-integration.md` +- `journey-use-cases/mfa-authentication-multi-method.md` +- `nodes/risk-management-nodes.md` +- `nodes/identity-management-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) +[PingOne Authorize](https://docs.pingidentity.com/pingoneauthorize/latest/overview.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/mfa-authentication-multi-method.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/mfa-authentication-multi-method.md new file mode 100644 index 0000000..778ce9f --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/mfa-authentication-multi-method.md @@ -0,0 +1,199 @@ +--- +title: "PingOne ST — Multi-Method MFA Authentication" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: ["mfa"] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Multi-Method MFA Authentication + +Design patterns for the multi-method MFA authentication inner journey, supporting up to 8 factors with per-method retry loops and recovery code fallbacks. Derived from CIAM Passwordless, Financial Services, and Threat Detection journey exports. + +## Scope + +**Covers:** Device selection, per-method authentication nodes, retry-loop-with-lockout, recovery code paths per method, inner journey structure. +**Does NOT cover:** MFA device registration — see `journey-use-cases/passwordless-mfa-registration.md`. Risk-based step-up routing — see `journey-use-cases/pingone-protect-risk-integration.md`. + +--- + +## Structure Overview + +MFA authentication is always implemented as a **dedicated inner journey** called from the main journey via `InnerTreeEvaluatorNode`. This keeps MFA logic reusable and independently updatable. + +**Main journey call:** +``` +InnerTreeEvaluatorNode (tree: MFA Authentication) + → true → proceed (MFA passed) + → false → FailureNode +``` + +--- + +## Device Selection + +**Entry pattern:** A `PageNode` presents the device selector. The selection drives outcomes via an embedded `ScriptedDecisionNode` ("Select MFA Device") that reads the user's registered device state from shared state and returns the chosen method as its outcome. + +**Observed outcomes:** `EMAIL` / `SMS` / `VOICE` / `FIDO2` / `OATH` / `PUSH` / `Magic Link` / `Error` + +Each outcome routes to a per-method authentication sub-path. + +--- + +## Per-Method Authentication Paths + +### OATH (TOTP) + +``` +PageNode ("Enter Verification Code") + children: ScriptedDecisionNode ("Validate Verification Code Input Script") + + OathTokenVerifierNode (isRecoveryCodeAllowed: true) + → successOutcome → SuccessNode + → failureOutcome → RetryLimitDecisionNode + → notRegisteredOutcome → GetAuthenticatorAppNode → OATH Registration inner journey + → recoveryCodeOutcome → PageNode ("Enter Recovery Code") → [recovery code path] +``` + +**OathTokenVerifierNode config:** `totpTimeInterval: 30`, `totpHashAlgorithm: HMAC_SHA1`, `totpTimeSteps: 2`, `maximumAllowedClockDrift: 5`, `isRecoveryCodeAllowed: true` + +### Push + +``` +PushAuthenticationSenderNode (mandatory: true, messageTimeout: 120000) + → SENT → PushWaitNode (secondsToWait: 5) + → DONE → PushResultVerifierNode + → TRUE → SuccessNode + → FALSE → FailureNode + → WAITING → loop back to PushWaitNode + → EXPIRED → RetryLimitDecisionNode → Retry → re-send push / Reject → FailureNode + → EXITED → PageNode (OTP fallback for users who dismiss the push) + → NOT_REGISTERED → GetAuthenticatorAppNode → Push Registration inner journey +``` + +### WebAuthn (FIDO2) + +``` +WebAuthnAuthenticationNode (isRecoveryCodeAllowed: true, asScript: true) + → success → SuccessNode + → unsupported → FailureNode + → noDevice → GetAuthenticatorAppNode → WebAuthn Registration inner journey + → failure → RetryLimitDecisionNode + → error → FailureNode + → recoveryCode → PageNode ("Enter Recovery Code") → [recovery code path, WEB_AUTHN typed] +``` + +### Email OTP + +``` +OneTimePasswordGeneratorNode (length: 6) + → ScriptedDecisionNode ("Send OTP Email") [calls IDM openidm.action() to deliver via email] + → PageNode ("Collect OTP") + children: ScriptedDecisionNode (UI validator) + OneTimePasswordCollectorDecisionNode (passwordExpiryTime: 5) + → true → SuccessNode + → false → RetryLimitDecisionNode → retry loop / Reject → FailureNode +``` + +### SMS / VOICE (Twilio Verify) + +``` +VerifyAuthIdentifierNode (identifierAttribute: telephoneNumber) + → True → VerifyAuthSenderNode (channel: SMS or CALL) + → true → PageNode ("Collect OTP") + children: VerifyAuthCollectorDecisionNode + → true → SuccessNode + → false → RetryLimitDecisionNode → retry loop + → error → FailureNode + → False / Error → FailureNode +``` + +### Magic Link (Email Suspend) + +``` +EmailSuspendNode (emailTemplateName: magicLinkTemplate, objectLookup: true) + → outcome → SuccessNode +``` + +The simplest path — journey suspends, user clicks the link in the email, journey resumes at SuccessNode. + +--- + +## Retry-Loop-With-Lockout Pattern + +Every code-entry path (OATH, OTP, recovery codes) uses this consistent retry loop: + +``` +[Failure outcome from verifier] + → RetryLimitDecisionNode (retryLimit: 3, incrementUserAttributeOnFailure: true) + → True (Retry) → ScriptedDecisionNode ("Set Invalid Code Error Message") + → [Input PageNode] ← reads invalidCodeErrorMessage from shared state + → False (Reject) → FailureNode +``` + +The `ScriptedDecisionNode` writes `invalidCodeErrorMessage` to shared state. The PageNode script reads it via `callbacksBuilder.textOutputCallback(2, invalidCodeErrorMessage)` and renders it inline on the next page load — no extra round-trip. + +--- + +## Recovery Code Paths + +Every MFA method that supports recovery codes follows the same recovery sub-path: + +``` +[MFA verifier node] → recoveryCodeOutcome + → PageNode ("Enter Recovery Code") + children: ScriptedDecisionNode ("Validate Recovery Code Input Script") + + RecoveryCodeCollectorDecisionNode (recoveryCodeType: ) + → True → SuccessNode + → False → RetryLimitDecisionNode → retry / Reject → FailureNode +``` + +**`recoveryCodeType` values by method:** + +| MFA method | recoveryCodeType | +|---|---| +| OATH / TOTP | `OATH` | +| WebAuthn | `WEB_AUTHN` | +| Push | `PUSH` | + +Mismatching `recoveryCodeType` to the authentication method causes validation failures. + +--- + +## Inner Journey Composition + +MFA authentication is kept as a separate inner journey. Never embed all MFA paths directly in the main journey graph — the resulting node count makes the journey unmanageable and prevents reuse. + +**Recommended structure:** +``` +Main journey + → InnerTreeEvaluatorNode (ThreatDetection) ← risk evaluation + → InnerTreeEvaluatorNode (MFADeviceRegistration) ← register device if not enrolled + → InnerTreeEvaluatorNode (MFAAuthentication) ← challenge with registered device + → SuccessNode +``` + +The `MFADeviceRegistration` inner journey is only needed in journeys that allow just-in-time registration. If all users must pre-enroll, it can be omitted. + +## Prerequisites + +- User must have at least one MFA device registered (or device registration inner journey must be in the chain before this one) +- Email notification service configured for Email OTP and Magic Link methods +- Twilio Verify credentials configured for SMS/VOICE methods +- FIDO2 requires HTTPS + +## Related references + +- `journey-use-cases/passwordless-mfa-registration.md` +- `journey-use-cases/pingone-protect-risk-integration.md` +- `nodes/mfa-nodes.md` +- `nodes/federation-contextual-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/password-reset-and-update.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/password-reset-and-update.md new file mode 100644 index 0000000..b706b97 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/password-reset-and-update.md @@ -0,0 +1,131 @@ +--- +title: "PingOne ST — Password Reset and Authenticated Update" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Password Reset and Authenticated Update + +Design patterns for unauthenticated password reset (email-gated) and authenticated password update (session-gated), derived from live AIC journey exports. + +## Scope + +**Covers:** Unauthenticated reset via email, authenticated password change with passwordless user branch, ValidatedPasswordNode settings, PatchObjectNode for password write. +**Does NOT cover:** Username recovery — see `journey-use-cases/account-recovery-and-username-reminder.md`. MFA reset — compose with `nodes/mfa-nodes.md`. + +--- + +## Pattern 1 — Unauthenticated Password Reset (Email-Gated) + +Used when the user does not have an active session. Identity is verified via email before a new password is accepted. + +**Node sequence:** +``` +PageNode (collect mail, required: true) + → IdentifyExistingUserNode (identityAttribute: mail, identifier: userName) + → true AND false → EmailSuspendNode (anti-enumeration) + → [journey suspends — user clicks link in email to resume] + → PageNode (collect new password, ValidatedPasswordNode, validateInput: true) + → PatchObjectNode (identityResource: managed/alpha_user) + → PATCHED → SuccessNode + → FAILURE → FailureNode +``` + +**Email template:** `resetPassword` — contains `{{object.resumeURI}}` as the reset link. Bilingual (English + French) in OOTB. + +**Anti-enumeration:** Both `IdentifyExistingUserNode` outcomes (`true` and `false`) route to the same `EmailSuspendNode`. User sees: `"If the details provided match our records, you will receive an email with further instructions."` + +**PatchObjectNode configuration:** +- `identityResource: managed/alpha_user` +- `patchAsObject: false` +- `ignoredFields: []` + +**ValidatedPasswordNode:** `validateInput: true` — new password is validated against the realm's password policy before `PatchObjectNode` writes it. + +--- + +## Pattern 2 — Authenticated Password Update (Session-Gated) + +Used when the user has an active session. No login form — the user is identified from the session. Handles both password-based and passwordless users. + +**Node sequence:** +``` +SessionDataNode (sessionDataKey: UserToken, sharedStateKey: userName) + → AttributePresentDecisionNode (presentAttribute: password) + → true (password user): PageNode ("Verify Existing Password") + → ValidatedPasswordNode (validateInput: false) + → DataStoreDecisionNode + → true: PageNode ("Update Password") + → false: FailureNode + → false (passwordless user): EmailSuspendNode + → [journey resumes when the user follows the resume URI in the email] + → PageNode ("Update Password") + → PageNode ("Update Password") + → ValidatedPasswordNode (validateInput: true) + → single outcome + → PatchObjectNode (ignoredFields: [userName]) + → PATCHED → SuccessNode + → FAILURE → FailureNode +``` + +**SessionDataNode** is the canonical entry for authenticated journeys — identifies the current user from the active session without a login form. + +**AttributePresentDecisionNode** branches on `password` attribute presence: +- `true` (password set) → require current-password verification before allowing change +- `false` (passwordless user) → gate with email verification instead + +**Two ValidatedPasswordNode instances:** + +| Instance | `validateInput` | Purpose | +|---|---|---| +| "Verify Existing Password" | `false` | Old credential — no policy enforcement | +| "Update Password" | `true` | New credential — policy enforced | + +**PatchObjectNode:** `ignoredFields: [userName]` — prevents the username from being changed via this flow. + +--- + +## Decision: Reset vs. Update + +| Criterion | Use Reset | Use Update | +|---|---|---| +| User has an active session | No | Yes | +| User forgot their password | Yes | No | +| Passwordless users need support | Both | Yes (via email gate) | +| Identity verification method | Email (resume URI) | Session (SessionDataNode) or email (passwordless branch) | + +--- + +## Security Considerations + +- Email resume URIs are single-use and time-limited — do not extend expiry without increasing rate limiting +- Password change should invalidate or re-issue the session (configure via `SetSessionProperties` or session termination post-patch) +- The `DataStoreDecisionNode` verify step in authenticated update prevents session hijacking from allowing a password change without knowing the current password +- Add `RetryLimitDecisionNode` after `DataStoreDecisionNode(false)` if you want to limit failed current-password attempts before locking + +## Prerequisites + +- Email notification service configured +- `resetPassword` email template present with `{{object.resumeURI}}` +- `managed/alpha_user` with `password`, `mail`, `userName` attributes accessible +- For authenticated update: active session with `UserToken` session property + +## Related references + +- `journey-use-cases/account-recovery-and-username-reminder.md` +- `nodes/basic-auth-nodes.md` +- `nodes/identity-management-nodes.md` +- `nodes/utility-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md new file mode 100644 index 0000000..dfab47f --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/passwordless-mfa-registration.md @@ -0,0 +1,209 @@ +--- +title: "PingOne ST — Passwordless MFA Device Registration" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: ["mfa"] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Passwordless MFA Device Registration + +Design patterns for the MFA device registration inner journey, supporting 7 factor types with per-method registration paths. Derived from CIAM Passwordless and Threat Detection journey exports. + +## Scope + +**Covers:** Device selection, per-method registration inner journeys (WebAuthn, OATH, Push, Combined OATH+PUSH, SMS, VOICE), RecoveryCodeDisplayNode placement, GetAuthenticatorAppNode usage, Twilio phone validation. +**Does NOT cover:** MFA authentication after registration — see `journey-use-cases/mfa-authentication-multi-method.md`. + +--- + +## Structure Overview + +Device registration is always a **dedicated inner journey** called from the registration or sign-on journey: + +``` +InnerTreeEvaluatorNode (tree: MFA Device Registration) + → true → proceed (device registered) + → false → FailureNode +``` + +The device registration inner journey delegates each method to its own nested inner journey via `InnerTreeEvaluatorNode`. This keeps registration paths independently updatable. + +--- + +## Device Selection + +**Entry:** A `PageNode` presents the device selection screen. The selection drives an embedded `ScriptedDecisionNode` or `ChoiceCollector` that returns the chosen method as its outcome. + +**Observed outcomes:** `Error` / `FIDO2` / `OATH` / `OATH+PUSH` / `PUSH` / `SMS` / `VOICE` + +Each outcome routes to a per-method registration inner journey. + +--- + +## Per-Method Registration Paths + +### FIDO2 / WebAuthn + +``` +InnerTreeEvaluatorNode (tree: Register WebAuthn Method) + → true → EmailTemplateNode ("Notify User On Email") → SuccessNode + → false → FailureNode +``` + +**Register WebAuthn Method inner journey:** +``` +WebAuthnRegistrationNode (userVerificationRequirement: PREFERRED, asScript: true, + generateRecoveryCodes: true, maxSavedDevices: 0, + acceptedSigningAlgorithms: [ES256, RS256], timeout: 60) + → success → RecoveryCodeDisplayNode → SuccessNode + → unsupported → FailureNode + → failure → FailureNode + → error → FailureNode +``` + +**`unsupported`, `failure`, and `error` all route to FailureNode** — no method fallback in the registration path. The device selection PageNode handles method fallback upstream. + +### OATH (TOTP) + +``` +InnerTreeEvaluatorNode (tree: Register OATH MFA) +``` + +**Register OATH MFA inner journey:** +``` +GetAuthenticatorAppNode + → outcome → OathRegistrationNode (algorithm: TOTP, passwordLength: SIX_DIGITS, + totpTimeInterval: 30, generateRecoveryCodes: true, + accountName: USERNAME, issuer: ForgeRock) + → OathDeviceStorageNode + → PageNode ("Collect Verification Code") + children: ScriptedDecisionNode (UI validator) + OathTokenVerifierNode + → successOutcome → RecoveryCodeDisplayNode → SuccessNode + → failureOutcome → RetryLimitDecisionNode → retry / Reject → FailureNode +``` + +**`GetAuthenticatorAppNode`** displays ForgeRock Authenticator download links before showing the QR code, ensuring the user has an app capable of scanning it. + +**Verification at registration:** After displaying the QR code, the user must enter a valid TOTP code to confirm the device was enrolled correctly. This prevents registration of misconfigured devices. + +### Push + +``` +InnerTreeEvaluatorNode (tree: Register Push MFA) +``` + +**Register Push MFA inner journey:** +``` +PushAuthenticationSenderNode (pushType: DEFAULT, mandatory: true, messageTimeout: 120000) + → NOT_REGISTERED → GetAuthenticatorAppNode + → OathRegistrationNode (registers push device) + → PushRegistrationNode + → successOutcome → RecoveryCodeDisplayNode → SuccessNode + → failureOutcome → FailureNode + → timeoutOutcome → RetryLimitDecisionNode → retry / Reject → FailureNode + → SENT → PushWaitNode → DONE → PushResultVerifierNode + → TRUE → SuccessNode (already registered, flow completes) +``` + +### Combined OATH + Push + +``` +InnerTreeEvaluatorNode (tree: Register OATH & PUSH) +``` + +**Register OATH & PUSH inner journey:** +``` +PushAuthenticationSenderNode + → NOT_REGISTERED → GetAuthenticatorAppNode + → CombinedMultiFactorRegistrationNode (algorithm: TOTP, + passwordLength: SIX_DIGITS, generateRecoveryCodes: true) + → successOutcome → RecoveryCodeDisplayNode → SuccessNode + → failureOutcome → FailureNode + → timeoutOutcome → RetryLimitDecisionNode → retry / Reject → FailureNode + → SENT → PushWaitNode → DONE → PushResultVerifierNode → TRUE → SuccessNode +``` + +`CombinedMultiFactorRegistrationNode` registers both TOTP and Push simultaneously in a single QR scan. + +### SMS / VOICE (Twilio Verify) + +``` +AttributePresentDecisionNode (presentAttribute: telephoneNumber) + → True → VerifyAuthLookupNode (phone number format/carrier validation) + → True → VerifyAuthSenderNode (channel: SMS or CALL) + → true → PageNode ("Collect OTP") + children: VerifyAuthCollectorDecisionNode + → true → PatchObjectNode ("Update User") → SuccessNode + → false → RetryLimitDecisionNode → retry / Reject → FailureNode + → error → FailureNode + → False / Error → FailureNode + → False → FailureNode (no phone number on file) +``` + +**`VerifyAuthLookupNode`** validates the phone number before sending — catches invalid numbers before incurring Twilio delivery cost. + +**`PatchObjectNode`** at the end of SMS/VOICE registration updates the user's profile with device enrollment metadata. + +--- + +## RecoveryCodeDisplayNode Placement + +**Rule:** `RecoveryCodeDisplayNode` must appear immediately after the registration node's `successOutcome`, before any other routing step. If the user exits before seeing the codes, they permanently lose access to them. + +``` +[Registration node] → successOutcome → RecoveryCodeDisplayNode → SuccessNode +``` + +This applies to: WebAuthn, OATH, Push, and Combined OATH+PUSH registrations. + +--- + +## EmailTemplateNode Post-Registration Notification + +After successful device registration, a non-blocking notification email is sent: + +``` +SuccessNode path + → EmailTemplateNode ("Notify User On Email", emailTemplateName: ...) + → EMAIL_SENT → SuccessNode + → EMAIL_NOT_SENT → SuccessNode ← notification is best-effort; does not block +``` + +Both `EMAIL_SENT` and `EMAIL_NOT_SENT` route to SuccessNode — notification failure does not block registration completion. + +--- + +## Prerequisites + +- User must be authenticated before device registration +- ForgeRock Authenticator app available to the user (links provided by `GetAuthenticatorAppNode`) +- Twilio Verify credentials configured for SMS/VOICE methods +- `telephoneNumber` attribute populated in the user's profile for SMS/VOICE enrollment +- HTTPS required for WebAuthn + +## Common variants + +| Variant | Note | +|---|---| +| Mandatory registration at first login | Place `MFA Device Registration` ITE after `CreateObjectNode(CREATED)` in registration journey | +| Optional registration | Wrap registration ITE with a `ChoiceCollector` offering "Register now" / "Skip" | +| Limit to one method | Remove method outcomes from device selection PageNode | +| Require verification code confirmation for OATH | Already in the OOTB pattern — `OathTokenVerifierNode` in the registration inner journey | + +## Related references + +- `journey-use-cases/mfa-authentication-multi-method.md` +- `nodes/mfa-nodes.md` +- `nodes/federation-contextual-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/pingone-protect-risk-integration.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/pingone-protect-risk-integration.md new file mode 100644 index 0000000..093119d --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/pingone-protect-risk-integration.md @@ -0,0 +1,197 @@ +--- +title: "PingOne ST — PingOne Protect Risk Integration" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: ["protect"] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — PingOne Protect Risk Integration + +Design patterns for integrating PingOne Protect risk evaluation into AIC journeys, derived from four production journey suites (CIAM Passwordless, Financial Services, Money Transfer, Threat Detection). + +## Scope + +**Covers:** Triple-node pattern, init/eval separation via shared state, production configuration, outcome routing, new device detection, account disable on high risk, ModifyAuthLevelNode step-up chain. +**Does NOT cover:** PingOne Protect service setup — see `ping-universal-services`. Transaction authorization — see `journey-use-cases/financial-services-step-up.md`. + +--- + +## Architecture: Inner Journey Wrapping + +PingOne Protect is always wrapped in a **dedicated "Threat Detection" inner journey** called from the main journey: + +``` +Main journey + → ScriptedDecisionNode ("Is Protect Analysis Required?", outcome: true/false) + → [true] InnerTreeEvaluatorNode ("Initialize P1 Protect") ← first call: init + → [after init] InnerTreeEvaluatorNode ("Protect Evaluation & Mitigation") ← second call: eval +``` + +Keeping Protect in an inner journey allows it to be reused across multiple main journeys (sign-on, registration, authorization) and updated independently. + +--- + +## Init/Eval Separation Pattern + +A single inner journey handles both initialization and evaluation via a shared state flag (`p1ProtectAction`): + +``` +Inner journey entry: +ScriptedDecisionNode ("P1 Protect Action?", reads p1ProtectAction from shared state) + → "init" → product-PingOneProtectInitializeNode + → true → SuccessNode + → false → FailureNode + → "eval" → [evaluation branch — see below] +``` + +The calling journey sets `p1ProtectAction` to `"init"` before the first call and `"eval"` before the second. This prevents duplicating the inner journey for two slightly different calls. + +--- + +## Evaluation Branch + +``` +"eval" path: +IdentifyExistingUserNode + → ScriptedDecisionNode ("Set UserId and Username For Protect") + [sets protectUserId and protectUsername in shared state] + → ScriptedDecisionNode ("Determine Flow Type", + outcomes: Authentication / Authorization / Registration) + → product-PingOneProtectEvaluationNode (per flow type) +``` + +**PingOneProtectEvaluationNode production configuration:** + +| Field | Value | Notes | +|---|---|---| +| `flowType` | `AUTHENTICATION` / `AUTHORIZATION` / `REGISTRATION` | Set from ScriptedDecisionNode output | +| `pauseBehavioralData` | `true` | | +| `storeEvaluateResult` | `true` | | +| `deviceSharingType` | `SHARED` | | +| `scoreThreshold` | `300` | Numeric threshold for `exceed` outcome | +| `userId` | `protectUserId` | Shared state key | +| `username` | `protectUsername` | Shared state key | +| `userType` | `EXTERNAL` | | +| `recommendedActions` | `[BOT_MITIGATION, AITM_MITIGATION, TEMP_EMAIL_MITIGATION]` | | + +--- + +## Standard Outcome Routing + +``` +product-PingOneProtectEvaluationNode + → low → product-PingOneProtectResultNode (SUCCESS) → SuccessNode + → medium → step-up MFA inner journey → (on MFA success) PingOneProtectResultNode (SUCCESS) + → high → ModifyAuthLevelNode (increment: 1) + → AccountActiveDecisionNode + → AccountLockoutNode (LOCK) + → EmailTemplateNode ("Send Account Disabled Email") + → product-PingOneProtectResultNode (FAILED) → FailureNode + → exceed / failure / clientError / BOT_MITIGATION / AITM_MITIGATION / TEMP_EMAIL_MITIGATION + → product-PingOneProtectResultNode (FAILED) → FailureNode +``` + +**Critical:** `product-PingOneProtectResultNode` must be called at **both** success and failure termination paths. Omitting it on one path degrades the Protect risk model over time. + +--- + +## New Device Detection Pattern + +Used in Financial Services and Money Transfer to send notifications without blocking the journey: + +``` +product-PingOneProtectEvaluationNode + → [after low/medium routing] + → ScriptedDecisionNode ("Extract Protect Activity Params", + outcomes: highRisk / newDevice / low_medRisk) + → newDevice: ScriptedDecisionNode ("Notify New Device Detected", + outcomes: sent / noMail / error) + → all outcomes → SuccessNode ← notification is best-effort + → highRisk: ScriptedDecisionNode ("Send Suspicious Activity Mail", + outcomes: sent / noMail / error) + → all outcomes → AccountLockoutNode (LOCK) → SuccessNode + → low_medRisk → SuccessNode +``` + +All notification outcomes route forward — a failed notification email never blocks the user. + +--- + +## ModifyAuthLevelNode + AuthLevelDecisionNode Step-Up Chain + +Used to trigger MFA only when risk warrants it: + +``` +product-PingOneProtectEvaluationNode + → medium / high → ModifyAuthLevelNode (authLevelIncrement: 1) + +[Back in main journey, after Threat Detection ITE:] +AuthLevelDecisionNode (threshold: 1) + → true (level elevated) → InnerTreeEvaluatorNode (MFA Authentication) + → false (level normal) → SuccessNode (skip MFA) +``` + +This decouples the risk signal (set in the inner journey) from the step-up decision (evaluated in the main journey). + +--- + +## PingOneProtectInitializeNode Configuration + +| Field | Production value | +|---|---| +| `behavioralDataCollection` | `true` | +| `enableTrust` | `false` | +| `disableTags` | `false` | +| `consoleLogEnabled` | `false` | +| `deviceKeyRsyncIntervals` | `14` | +| `disableHub` | `false` | +| `lazyMetadata` | `false` | +| `deviceAttributesToIgnore` | `[]` | + +Cannot be placed inside a PageNode. + +--- + +## Multiple Flow Types in One Journey + +Production journeys use multiple `product-PingOneProtectEvaluationNode` instances for different flow types: +- `AUTHENTICATION` — for login flows +- `AUTHORIZATION` — for transaction/step-up flows +- `REGISTRATION` — for new user registration + +The `Determine Flow Type` ScriptedDecisionNode routes to the correct instance based on a shared state flag set earlier in the journey. + +## Prerequisites + +- PingOne Protect service enabled in the tenant environment +- Worker service ID configured in the Protect node +- Risk policy configured in PingOne Protect +- PingOne SDK 4.4.0+ on the client (required for behavioral data collection) +- `IdentifyExistingUserNode` run before evaluation (to populate `userId` for Protect) + +## Common variants + +| Variant | Note | +|---|---| +| Registration only | Use `flowType: REGISTRATION`; medium risk may block registration rather than trigger MFA | +| No account disable on high risk | Remove `AccountLockoutNode` from high-risk path; route to MFA step-up or FailureNode instead | +| Evaluation without init (returning session) | Set `p1ProtectAction: "eval"` without init for users with an existing Protect session | + +## Related references + +- `journey-use-cases/mfa-authentication-multi-method.md` +- `journey-use-cases/financial-services-step-up.md` +- `nodes/risk-management-nodes.md` + +## Source + +[PingOne Protect Evaluation node](https://docs.pingidentity.com/auth-node-ref/latest/pingone/pingone-protect-evaluation.html) +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/progressive-profiling.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/progressive-profiling.md new file mode 100644 index 0000000..bf2cc88 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/progressive-profiling.md @@ -0,0 +1,156 @@ +--- +title: "PingOne ST — Progressive Profiling" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Progressive Profiling + +Design patterns for progressively collecting user profile data after login, without blocking authentication. Derived from the OOTB progressive profiling journey template. + +## Scope + +**Covers:** `LoginCountDecisionNode` trigger configuration, `QueryFilterDecisionNode` SCIM filter syntax, dual-gate pattern, `IncrementLoginCountNode` placement, `PatchObjectNode` for preference updates. +**Does NOT cover:** Registration-time attribute collection — see `nodes/identity-management-nodes.md`. Full profile management — see `journey-use-cases/social-and-local-registration-authentication.md`. + +--- + +## Pattern: Dual-Gate Progressive Profiling + +Progressive profiling should only prompt when two conditions are both true: +1. The user is at the right point in their lifecycle (e.g., 2nd login) +2. The data is actually missing + +``` +LoginCountDecisionNode (interval: AT, amount: 2) + → true → QueryFilterDecisionNode (filter: attribute missing or incomplete) + → true → PageNode (collect missing attributes) + → PatchObjectNode → SuccessNode + → false → SuccessNode (data already present — skip) + → false → SuccessNode (wrong login count — skip) +``` + +This pattern ensures users are never re-prompted once they have provided the data, and are not interrupted on every login. + +--- + +## LoginCountDecisionNode Configuration + +| Field | Value | Notes | +|---|---|---| +| `interval` | `AT` or `EVERY` | `AT`: trigger exactly at count N (one-time). `EVERY`: trigger at count N and every login after. | +| `amount` | `2` (OOTB) | Any positive integer | +| `identityAttribute` | `userName` | IDM attribute used to retrieve the user object | + +- Outcomes: **True** (count matches) / **False** (count does not match) +- `AT` is preferred for one-time prompts — prevents the prompt from firing on every login after count N + +--- + +## QueryFilterDecisionNode — SCIM Filter Syntax + +Evaluates a SCIM/LDAP-style filter against the user's profile attributes to determine if data is missing. + +**OOTB filter for preferences:** +``` +!(/preferences pr) or /preferences/marketing eq false or /preferences/updates eq false +``` + +This matches users who: +- Have no `preferences` attribute at all (`!(/preferences pr)`) +- OR have `preferences/marketing` set to `false` (i.e., not yet opted in) +- OR have `preferences/updates` set to `false` + +**Outcomes:** **True** (filter matches — data missing or incomplete) / **False** (filter does not match — data present) + +**Dot-path notation for nested attributes:** Use `/` as the path separator for nested attributes: `/preferences/marketing`, `/preferences/updates`. + +--- + +## AttributeCollectorNode for Nested Attributes + +When collecting nested attributes, use dot-path notation in `attributesToCollect`: + +```json +"attributesToCollect": ["preferences/updates", "preferences/marketing"] +``` + +The attribute must be defined in the identity schema before it can be collected. `required: false` allows users to skip optional fields. + +--- + +## PatchObjectNode for Preference Updates + +``` +PatchObjectNode + identityResource: managed/alpha_user + patchAsObject: false + ignoredFields: [] +``` + +- Outcomes: **PATCHED** / **FAILURE** +- `patchAsObject: false` merges only the specified attributes — does not overwrite the entire user object +- `FAILURE`: route to FailureNode (or silently log and continue, depending on data criticality) + +--- + +## IncrementLoginCountNode Placement + +**Critical:** Place `IncrementLoginCountNode` immediately after `CreateObjectNode(CREATED)` in the registration journey so the login count starts at 1 after registration. + +``` +CreateObjectNode → CREATED → IncrementLoginCountNode → SuccessNode +``` + +Without this, the count is 0 after registration. `LoginCountDecisionNode(AT=2)` will then fire on the 3rd visit (2nd login), not the 2nd. + +--- + +## Inner Journey Pattern + +Progressive profiling should be an **inner journey** called from the main sign-in journey: + +``` +Main Sign-In journey (after authentication success) + → InnerTreeEvaluatorNode (tree: Progressive Profiling) + → true → SuccessNode + → false → FailureNode (rare — only if PatchObjectNode fails) +``` + +Keeping it separate allows profiling to be updated independently and disabled without modifying the login journey. + +--- + +## Common variants + +| Variant | Notes | +|---|---| +| Trigger once, any login after N | Use `interval: EVERY` instead of `AT` | +| Trigger at multiple lifecycle points | Use multiple `LoginCountDecisionNode` instances with different amounts and different `QueryFilterDecisionNode` filters | +| Require mandatory data before access | Route `QueryFilterDecisionNode(true)` to collection before SuccessNode; remove the skip path | +| Collect a single attribute | Use a single-attribute `AttributeCollectorNode` in the PageNode | + +## Prerequisites + +- `IncrementLoginCountNode` placed in the registration journey so login count is initialized +- Custom attributes (`preferences/marketing`, etc.) defined in the IDM managed object schema before collection +- `managed/alpha_user` with write access for `PatchObjectNode` + +## Related references + +- `nodes/identity-management-nodes.md` +- `nodes/utility-nodes.md` +- `journey-use-cases/social-and-local-registration-authentication.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/social-and-local-registration-authentication.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/social-and-local-registration-authentication.md new file mode 100644 index 0000000..3857005 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/journey-use-cases/social-and-local-registration-authentication.md @@ -0,0 +1,182 @@ +--- +title: "PingOne ST — Social and Local Registration and Authentication" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["customer"] +doc_type: guide +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Social and Local Registration and Authentication + +Design patterns for CIAM registration and authentication journeys supporting both local credentials and social identity providers. Derived from CIAM Passwordless and basic registration journey exports. + +## Scope + +**Covers:** Social + local choice entry, `SocialProviderHandlerNodeV2` outcomes, social registration profile collection, `CreateObjectNode` with cleanup, email verification gate, T&C lifecycle, `AttributeValueDecisionNode`. +**Does NOT cover:** MFA registration after account creation — see `journey-use-cases/passwordless-mfa-registration.md`. Progressive profiling — see `journey-use-cases/progressive-profiling.md`. + +--- + +## Entry: Social + Local Choice + +``` +PageNode (outcomes: localAuthentication / socialAuthentication) + → localAuthentication → Platform Username → Platform Password → DataStoreDecisionNode + → socialAuthentication → SocialProviderHandlerNodeV2 +``` + +The entry `PageNode` embeds a `ScriptedDecisionNode` or `ChoiceCollector` that drives the `localAuthentication` / `socialAuthentication` outcomes. This pattern allows a single journey to serve both local and social users. + +--- + +## SocialProviderHandlerNodeV2 + +**Configuration:** +- Select from IdPs registered in the realm's Social Identity Provider service +- `transformScript`: maps incoming IdP claims to managed object attributes + +- Outcomes: **ACCOUNT_EXISTS** / **NO_ACCOUNT** / **SOCIAL_AUTH_INTERRUPTED** + +**Per outcome routing:** + +| Outcome | Action | +|---|---| +| `ACCOUNT_EXISTS` | Continue to authentication or profile completion | +| `NO_ACCOUNT` | Route to social registration profile collection | +| `SOCIAL_AUTH_INTERRUPTED` | Route to FailureNode | + +--- + +## Social Registration Path + +When `NO_ACCOUNT`: collect a minimal profile and create the user object. + +``` +SocialProviderHandlerNodeV2 → NO_ACCOUNT + → PageNode (social registration profile fields) + children: AttributeCollectorNode (mail, givenName, sn — required: false, validateInputs: false) + + [optional: ValidatedUsernameNode, AcceptTermsAndConditionsNode] + → RequiredAttributesPresentNode + → True → CreateObjectNode (identityResource: managed/alpha_user) + → CREATED → IncrementLoginCountNode → proceed + → FAILURE → ScriptedDecisionNode ("Delete User Entry") → FailureNode + → False → [re-display form or FailureNode] +``` + +**`RequiredAttributesPresentNode`** guards `CreateObjectNode` — prevents partial user creation when required fields are missing. + +**`CreateObjectNode(FAILURE)` cleanup:** Always follow `FAILURE` with a `ScriptedDecisionNode` that deletes any partial record (`openidm.delete("managed/alpha_user", ...)`) before routing to FailureNode. Prevents orphaned partial accounts from blocking future registrations with the same email. + +--- + +## Local Registration Path (Basic) + +``` +PageNode (single screen) + children: AttributeCollectorNode (mail, givenName, sn) + + ValidatedUsernameNode (validateInput: true) + + ValidatedPasswordNode (validateInput: true) + + AcceptTermsAndConditionsNode + → CreateObjectNode (identityResource: managed/alpha_user) + → CREATED → IncrementLoginCountNode → SuccessNode + → FAILURE → FailureNode +``` + +**Notes:** +- Co-locating `AcceptTermsAndConditionsNode` in the registration PageNode records T&C acceptance at the point of signup +- `validateInput: true` on both Username and Password enforces policy before object creation +- OOTB registration has no email verification — add `EmailSuspendNode` after `CreateObjectNode(CREATED)` if email verification is required + +--- + +## Email Verification Gate (Authentication Journey) + +After registration without email verification, the authentication journey gates unverified users: + +``` +AttributeValueDecisionNode ("Is Email Verified?", attribute: emailVerified, value: true) + → false → [email verification sub-journey] + → EmailSuspendNode (emailTemplateName: emailVerification) + → ScriptedDecisionNode ("Set Email Verification Status To True") + → PatchObjectNode (set emailVerified: true) + → continue + → true → continue +``` + +The `ScriptedDecisionNode` after `EmailSuspendNode` updates the flag before `PatchObjectNode` writes it — do not rely on `PatchObjectNode` alone. + +--- + +## T&C Lifecycle in Authentication + +Authentication journeys enforce current T&C version at each sign-in: + +``` +TermsAndConditionsDecisionNode + → false (not accepted or outdated version) → AcceptTermsAndConditionsNode → continue + → true (accepted and current) → continue +``` + +When a new T&C version is published, all existing users are prompted on their next login. Users who skip login are not prompted until they return. + +--- + +## Write Federation Information node + +After social authentication with an existing account, persist the federation link: + +``` +SocialProviderHandlerNodeV2 → ACCOUNT_EXISTS + → WriteFederationInformationNode → continue +``` + +This stores the external IdP subject identifier linked to the local account, enabling future social logins to resolve to the same user without re-prompting for profile data. + +--- + +## Duplicate Account Handling + +The OOTB `CreateObjectNode(FAILURE)` path does not distinguish between "email already in use" and other failures. For production CIAM: + +1. Run `IdentifyExistingUserNode` (by `mail`) before `CreateObjectNode` +2. If `true` (account exists): route to account-linking flow or error message +3. If `false` (new user): proceed to `CreateObjectNode` + +This prevents confusing failure messages and enables account linking for users who previously registered locally and now attempt social registration. + +--- + +## Prerequisites + +- Social Identity Providers configured in realm Social Identity Provider service +- `managed/alpha_user` schema includes `mail`, `givenName`, `sn`, `userName`, `emailVerified` attributes +- Email notification service configured for email verification flow +- `emailVerification` email template present with `{{object.resumeURI}}` + +## Common variants + +| Variant | Notes | +|---|---| +| Email verification required at registration | Add `EmailSuspendNode` immediately after `CreateObjectNode(CREATED)` before `SuccessNode` | +| Social-only (no local) | Remove `localAuthentication` branch from entry PageNode | +| Profile completion after social login | Add `AttributeCollectorNode` after `ACCOUNT_EXISTS` if profile is incomplete | +| Multiple social providers | Add outcomes to entry `PageNode` per provider; each routes to a separate `SocialProviderHandlerNodeV2` instance | + +## Related references + +- `journey-use-cases/passwordless-mfa-registration.md` +- `journey-use-cases/progressive-profiling.md` +- `nodes/identity-management-nodes.md` +- `nodes/basic-auth-nodes.md` + +## Source + +[Authentication nodes — PingOne AIC](https://docs.pingidentity.com/pingoneaic/journeys/auth-nodes.html) +[Social Provider Handler node](https://docs.pingidentity.com/auth-node-ref/latest/social-provider-handler.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/basic-auth-nodes.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/basic-auth-nodes.md new file mode 100644 index 0000000..b0e7a01 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/basic-auth-nodes.md @@ -0,0 +1,195 @@ +--- +title: "PingOne ST — Basic Authentication Nodes" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Basic Authentication Nodes + +Core nodes for credential collection, user lookup, and validation against identity stores. + +## Scope + +**Covers:** Username/password collection, credential validation, session-based user identification, attribute branching, and terminal outcomes. +**Does NOT cover:** MFA — see `nodes/mfa-nodes.md`. Flow control — see `nodes/utility-nodes.md`. + +--- + +## Credential Collection + +### Platform Username node (`ValidatedUsernameNode`) +Collects the username from the user. Writes to `username` in shared state. **Use this in AIC — do not use the deprecated Username Collector node.** + +**Configuration:** + +| Field | Notes | +|---|---| +| `validateInput` | `true` enforces IDM username policy (format, length) on submission | +| `usernameAttribute` | The IDM attribute storing the username (default: `userName`) | +| `autocompleteValues` | If set, provides HTML5 autocomplete suggestions for the input field | + +- Outcomes: single (proceeds to next node) +- Must be a PageNode child — standalone placement renders no input field + +### Platform Username node V2 (`ValidatedUsernameNodeV2`) +V2 adds prepopulation support. Use V2 when the journey may have the username already in state (e.g., re-authentication after session expiry, step-up flows). + +**Additional field vs V1:** + +| Field | Notes | +|---|---| +| `prepopulate` | `true` — input field is prepopulated with the `username` value from node state if present | + +- All other configuration identical to V1 +- Outcomes: single + +### Platform Password node (`ValidatedPasswordNode`) +Collects the password from the user. Writes to `password` in transient state. **Use this in AIC — do not use the deprecated Password Collector node.** + +**Configuration:** + +| Field | Notes | +|---|---| +| `validateInput` | `true` enforces IDM password policy on submission; `false` skips policy check (for existing credential verification) | +| `passwordAttribute` | The IDM attribute storing the password (default: `password`) | + +- Outcomes: single +- Must be a PageNode child + +**Two-instance pattern (password update journeys):** +- Instance 1 — "Verify Existing Password": `validateInput: false` (existing credential, no policy enforcement) +- Instance 2 — "New Password": `validateInput: true` (new credential, policy enforced) + +### Zero Page Login Collector node +Captures username and password in a single step without presenting an intermediate page. Useful when the client has already collected credentials. + +- Outcomes: single + +> **AIC note:** `Username Collector` and `Password Collector` nodes are not compatible with Advanced Identity Cloud. Always use `Platform Username` (`ValidatedUsernameNode`) and `Platform Password` (`ValidatedPasswordNode`) instead. + +--- + +## Credential Validation + +### Data Store Decision node +Validates username + password against the realm's configured data store. + +- Outcomes: **True** (credentials match) / **False** (credentials do not match) +- No configurable properties — operates on the realm's default data store +- Pair with `RetryLimitDecisionNode` to allow multiple attempts before lockout +- Used in authenticated password-update journeys to verify the current password before allowing a change + +**Contrast with IdentityStoreDecisionNode:** DataStoreDecisionNode returns only True/False. Use `IdentityStoreDecisionNode` when you need to handle LOCKED, EXPIRED, or CANCELLED states explicitly. + +### Identity Store Decision node +Authenticates against the cloud identity store with granular lifecycle outcomes. + +- Outcomes: **TRUE** / **FALSE** / **LOCKED** / **EXPIRED** / **CANCELLED** +- Wire each outcome explicitly: + - `LOCKED` → account-locked message or recovery path + - `EXPIRED` → password-change inner journey + - `CANCELLED` → FailureNode +- Used in financial-grade and CIAM journeys where account lifecycle states must be handled distinctly + +### LDAP Decision node +Authenticates against an external LDAP or Active Directory server. Supports password policy outcomes not available in Data Store Decision. + +- Outcomes: **True** / **False** / password policy outcomes (e.g., `EXPIRED`, `LOCKED`) +- Use when the realm is connected to an external LDAP/AD and password-policy-aware branching is needed + +### AD Decision node +Authenticates specifically against Active Directory. + +- Outcomes: **True** / **False** + +### Passthrough Authentication node +Authenticates a user against a third-party system via an ICF connector without requiring the user to re-enter credentials. The credentials are already in journey state. + +**Configuration:** + +| Field | Notes | +|---|---| +| `systemEndpoint` | Name of the ICF connector to use (configured in IDM) | +| `objectType` | ICF object type for the authenticating user | +| `identityAttribute` | The IDM attribute used as the username for authentication | +| `passwordAttribute` | The IDM attribute used as the password for authentication | + +- Outcomes: **Authenticated** / **Failed** / **Missing Input** +- `Missing Input`: username or password not present in journey state — route back to credential collection +- Use during directory migration to validate credentials against a legacy system while the primary directory is being replaced + +--- + +## Session-Based User Identification + +### Get Session Data node (`SessionDataNode`) +Extracts a value from the current active session into shared state, enabling authenticated journeys to identify the user without a login form. + +**Production configuration:** +- `sessionDataKey: UserToken` — the session property containing the user token +- `sharedStateKey: userName` — the shared state key to write the username to + +- Outcomes: single +- **Canonical entry pattern for authenticated journeys** (password update, profile management): place as the first node; the extracted `userName` is then available for `DataStoreDecisionNode`, `PatchObjectNode`, and `IdentifyExistingUserNode` downstream + +--- + +## Attribute-Based Branching + +See `nodes/identity-management-nodes.md` → Attribute Present Decision node and Attribute Value Decision node for attribute-based branching. + +--- + +## Terminal Nodes + +### Success node +Ends the journey successfully. Creates an authenticated session. Every journey must have at least one Success node. + +### Failure node +Ends the journey with an authentication failure. No session created. Use at the end of any path that should deny access. + +--- + +## Common patterns + +| Pattern | Nodes | +|---|---| +| Basic login | PageNode(ValidatedUsername + ValidatedPassword) → DataStoreDecisionNode → Success / Failure | +| Login with retry | DataStoreDecisionNode(False) → RetryLimitDecisionNode → retry loop or Failure | +| Full lifecycle login | PageNode(ValidatedUsername + ValidatedPassword) → IdentityStoreDecisionNode(TRUE/LOCKED/EXPIRED/CANCELLED/FALSE) | +| Authenticated journey entry | SessionDataNode → AttributePresentDecisionNode | +| Passwordless user branch | AttributePresentDecisionNode(`password` = False) → EmailSuspendNode / (True) → DataStoreDecisionNode | +| Email verification gate | AttributeValueDecisionNode(False) → email verification inner journey → (true) proceed | + +## Prerequisites + +- Identity store (IDM managed object schema) configured and accessible. +- Realm data store or LDAP/AD connector configured for credential validation. + +## Common variants + +- **LDAP-backed realm:** use `LDAPDecisionNode` instead of `DataStoreDecisionNode`; LDAP returns additional password-policy outcomes. +- **Active Directory passthrough:** use `ADDecisionNode` for AD-specific NTLM/Kerberos validation. + +## Related references + +- `nodes/mfa-nodes.md` +- `nodes/utility-nodes.md` +- `nodes/risk-management-nodes.md` +- `nodes/identity-management-nodes.md` +- `journey-use-cases/password-reset-and-update.md` + +## Source + +[Basic authentication nodes](https://docs.pingidentity.com/auth-node-ref/latest/overview.html) +[Data Store Decision node](https://docs.pingidentity.com/auth-node-ref/latest/data-store-decision.html) +[Platform Username node](https://docs.pingidentity.com/auth-node-ref/latest/platform-username.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/federation-contextual-nodes.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/federation-contextual-nodes.md new file mode 100644 index 0000000..5a74787 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/federation-contextual-nodes.md @@ -0,0 +1,257 @@ +--- +title: "PingOne ST — Federation and Contextual Nodes" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Federation and Contextual Nodes + +Nodes for federated identity (social login, SAML, OIDC), external OTP delivery (Twilio), device context, cookies, certificates, and behavioral signals. + +## Scope + +**Covers:** Social Provider Handler V2, SAML2, OIDC validator, Twilio Verify nodes, device profiling/geofencing, cookie decisions, certificate auth, and login count behavioral nodes. +**Does NOT cover:** App registration or IdP configuration — see `ping-foundation` → `app-setup.md`. + +--- + +## Social / Federation Nodes + +### Social Provider Handler node (V2) +Initiates authentication via a configured social identity provider or any OIDC-compliant external IdP. + +**Configuration:** +- Select from IdPs registered in the realm's Social Identity Provider service +- `transformScript`: optional script to map incoming claims to managed object attributes + +- Outcomes: **ACCOUNT_EXISTS** / **NO_ACCOUNT** / **SOCIAL_AUTH_INTERRUPTED** +- `ACCOUNT_EXISTS` → continue to authentication or profiling flow +- `NO_ACCOUNT` → profile collection PageNode → `CreateObjectNode` for social registration +- `SOCIAL_AUTH_INTERRUPTED` → FailureNode + +**Entry pattern (social + local choice):** +``` +PageNode (outcomes: localAuthentication / socialAuthentication) + → localAuthentication: Platform Username → Platform Password → DataStoreDecisionNode + → socialAuthentication: SocialProviderHandlerNodeV2 +``` + +See `journey-use-cases/social-and-local-registration-authentication.md` for full pattern. + +### OIDC ID Token Validator node +Validates a JWT ID token from an external OpenID Connect provider. Extracts claims into shared state. + +- Outcomes: **Valid** / **Invalid** +- Use when the client presents a pre-obtained ID token (e.g., native app silent authentication) + +### SAML2 Authentication node +Initiates or processes a SAML 2.0 authentication exchange with an external IdP. + +- Outcomes: **Success** / **Failure** / **Account not found** + +### Write Federation Information node +Persists federation-related data (e.g., external IdP subject, linked account) to the user's managed object. + +- Outcomes: single +- Place after Social Provider Handler or SAML2 node on the account-linking path + +### Select Identity Provider node +Presents the user with a list of identity providers to choose from (for multi-IdP login pages). + +- Outcomes: one per configured IdP + +--- + +## Twilio Verify Nodes + +Used for SMS and voice OTP delivery in MFA registration and authentication flows. + +### Twilio Verify Identifier node (`VerifyAuthIdentifierNode`) +Validates that a phone number attribute is available before sending via Twilio. + +**Configuration:** + +| Field | Observed values | Notes | +|---|---|---| +| `identifierAttribute` | `telephonenumber` / `telephoneNumber` | Case varies — check your schema attribute name | +| `identifierSharedState` | `userIdentifier` | Shared state key for the resolved phone number | + +- Outcomes: **True** / **False** / **Error** +- `False`: phone number missing — route to FailureNode or prompt user to add a phone number +- Place before `VerifyAuthSenderNode` — do not attempt to send if identifier lookup fails + +### Twilio Verify Sender node (`VerifyAuthSenderNode`) +Sends an OTP to the user's phone via SMS or voice call. + +**Configuration:** + +| Field | Observed values | +|---|---| +| `channel` | `SMS` or `CALL` | +| `accountSID` | Twilio account SID | +| `authToken` | Twilio auth token | +| `serviceSID` | Twilio Verify service SID | +| `requestIdentifier` | `false` | + +- Outcomes: **true** / **error** + +### Twilio Verify Lookup node (`VerifyAuthLookupNode`) +Validates the phone number format and carrier information before MFA device registration. + +- Outcomes: **True** / **False** / **Error** +- Use before `VerifyAuthSenderNode` in device registration flows (CIAM Passwordless MFA Device Registration) + +### Twilio Verify Collector Decision node (`VerifyAuthCollectorDecisionNode`) +Collects and verifies the OTP code the user received via Twilio. + +**Configuration:** + +| Field | Observed value | +|---|---| +| `hideCode` | `true` | +| `showResendButton` | `false` | +| `showCancelButton` | `false` | +| `identifierSharedState` | `userIdentifier` | + +- Outcomes: **true** / **false** / **error** + +**Full Twilio SMS/VOICE path:** +``` +VerifyAuthIdentifierNode(True) + → VerifyAuthSenderNode(true) + → PageNode(VerifyAuthCollectorDecisionNode) + → true → proceed + → false → RetryLimitDecisionNode → retry loop / Failure + → error → FailureNode +``` + +--- + +## Contextual Nodes + +### Device Profile Collector node +Collects device characteristics (browser, OS, IP, screen dimensions, fonts) silently from the client. + +- Outcomes: single — no visible UI interaction + +### Device Match node +Compares the current device profile against previously stored profiles for this user. + +- Outcomes: **True** (known device) / **False** (unknown device) + +### Device Geofencing node +Evaluates whether the user's device is within configured geographic boundaries. + +- Outcomes: **True** (inside fence) / **False** (outside fence) + +### Device Location Match node +Checks whether the current device location matches the user's last known location within a configured radius. + +- Outcomes: **True** / **False** + +### Device Tampering Verification node +Checks Ping SDK signals for device compromise (rooted/jailbroken). + +- Outcomes: **True** (device appears legitimate) / **False** (tampering detected) + +### Device Profile Save node +Saves the current device profile to the user's stored device list after successful authentication. + +- Outcomes: single + +--- + +## Cookie Nodes + +### Cookie Presence Decision node +Checks whether a specific cookie is present in the incoming request. + +- Outcomes: **True** (cookie present) / **False** (cookie absent) + +### Persistent Cookie Decision node +Validates a Ping Identity persistent authentication cookie. + +- Outcomes: **True** (valid cookie — user can skip re-authentication) / **False** (no valid cookie) + +### Set Persistent Cookie node +Issues a persistent authentication cookie to the client after successful authentication. + +- Outcomes: single + +### Set Custom Cookie node +Sets an arbitrary cookie on the client response. + +- Outcomes: single + +--- + +## Certificate Nodes + +### Certificate Collector node +Prompts the client to present a TLS client certificate. Stores it in transient state. + +- Outcomes: single + +### Certificate Validation node +Validates the collected certificate against configured trust anchors and CRL/OCSP. + +- Outcomes: **True** (certificate valid) / **False** (certificate invalid or untrusted) + +### Certificate User Extractor node +Extracts a username from the client certificate's Subject or SAN field and writes to shared state. + +- Outcomes: single + +--- + +## Behavioral Nodes + +For login-count-based routing and incrementing the login counter, see `nodes/utility-nodes.md` → Login Count Decision node and Increment Login Count node. + +--- + +## Common patterns + +| Pattern | Nodes | +|---|---| +| Social login with registration fallback | Social Provider Handler V2(NO_ACCOUNT) → Attribute Collector → Create Object | +| Social + local choice at journey entry | PageNode(localAuthentication/socialAuthentication) → respective paths | +| Twilio SMS MFA | VerifyAuthIdentifier → VerifyAuthSender → PageNode(VerifyAuthCollector) → retry loop | +| Device trust check | Device Profile Collector → Device Match(False) → MFA step-up | +| Persistent cookie SSO | Persistent Cookie Decision(True) → Success / (False) → login journey | +| First-login onboarding | Login Count Decision(AT=1, True) → onboarding inner journey → Increment Login Count | +| Certificate-based auth | Certificate Collector → Certificate Validation → Certificate User Extractor → Success | + +## Prerequisites + +- Social identity providers (Google, Apple, Facebook, etc.) registered in the Social Identity Provider service within the realm. +- Twilio credentials (account SID, auth token, Verify service SID) configured before using Twilio Verify nodes. +- SAML2 external IdP metadata imported and the realm's SAML2 service enabled for `SAML2AuthenticationNode`. + +## Common variants + +- **Enterprise social login:** use OIDC ID Token Validator instead of Social Provider Handler when a native app supplies a pre-obtained ID token. +- **Device trust for step-up:** combine Device Profile Collector → Device Match → `AuthLevelDecision` to skip MFA for trusted devices. + +## Related references + +- `nodes/basic-auth-nodes.md` +- `nodes/identity-management-nodes.md` +- `nodes/mfa-nodes.md` +- `journey-use-cases/social-and-local-registration-authentication.md` +- `journey-use-cases/mfa-authentication-multi-method.md` + +## Source + +[Federation nodes](https://docs.pingidentity.com/auth-node-ref/latest/overview.html) +[Social Provider Handler node](https://docs.pingidentity.com/auth-node-ref/latest/social-provider-handler.html) +[Device Profile Collector node](https://docs.pingidentity.com/auth-node-ref/latest/device-profile-collector.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/identity-management-nodes.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/identity-management-nodes.md new file mode 100644 index 0000000..4850f09 --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/identity-management-nodes.md @@ -0,0 +1,336 @@ +--- +title: "PingOne ST — Identity Management Nodes" +product_family: pingone-st +products: ["pingone-aic", "pingam", "pingidm"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Identity Management Nodes + +Nodes for reading, writing, and managing user identity data during authentication journeys — registration, profile collection, consent, KBA, object operations, and social federation. + +## Scope + +**Covers:** Attribute collection and validation, user object creation/update/lookup, consent, KBA, profile completeness, social login, and PingOne Verify integration. +**Does NOT cover:** Directory setup or managed object schema — see `ping-foundation` → `directory-setup.md`. Deep provisioning and reconciliation patterns — outside the scope of this reference. + +--- + +## User Lookup + +### Identify Existing User node +Looks up an existing user in the identity store by a specified attribute and writes the found user's identifier to shared state. + +**Configuration:** + +| Field | Purpose | +|---|---| +| `identityAttribute` | The attribute used to look up the user in the identity store (e.g., `mail`) | +| `identifier` | The shared state key populated with the found user's identifier (e.g., `userName`) | + +- Outcomes: **True** (user found) / **False** (user not found) + +**Anti-enumeration wiring:** In recovery and reset journeys, wire both `True` and `False` outcomes to the same successor node (typically `EmailSuspendNode`). The user sees an identical message regardless of whether an account exists. Never route `True` and `False` to different user-facing messages — that leaks account existence. + +**Multiple uses per journey:** Used both as an entry lookup (recovery journeys look up by `mail`) and as an in-journey user context loader (MFA inner journeys load user context before PingOne Protect evaluation). + +### Display Username node +Displays the authenticated or recovered user's username on the current page. + +- Configuration: `userName: userName`, `identityAttribute: mail` +- Outcomes: single — no branching +- Used in OOTB Account Recovery to show the recovered username in the browser after email verification, before routing to the Login inner journey +- Contrast with the Forgotten Username journey, which sends the username in the email body instead of displaying it in-browser + +--- + +## Attribute Collection + +### Attribute Collector node +Prompts the user to provide values for specified identity schema attributes. + +**Configuration:** + +| Field | Notes | +|---|---| +| `attributesToCollect` | List of schema attribute names; must be present and viewable in identity schema | +| `required` | When `true`, all fields are mandatory (node name in config: `required`, label: "All Attributes Required") | +| `validateInputs` | When `true`, enforces IDM schema policy against submitted values | +| `identityAttribute` | Attribute used to identify the managed object (default: `userName`) | + +- Outcomes: single (attribute values stored in shared state) +- Supports dot-path notation for nested attributes: `preferences/marketing`, `preferences/updates` +- Must be a PageNode child — standalone AttributeCollector renders no fields (see `nodes/utility-nodes.md` Rule 1) + +### Attribute Present Decision node +Checks whether a specific attribute exists on the managed user object (regardless of whether the field is private). + +**Configuration:** + +| Field | Notes | +|---|---| +| `presentAttribute` | The object attribute to verify is present (e.g., `password`) | +| `identityAttribute` | The attribute to query in the IDM object (e.g., `userName`) | + +- Outcomes: **True** (attribute present) / **False** (attribute absent) +- Use case: detect passwordless users — `presentAttribute: password` returns `False` for users without a password set, enabling conditional branching to email-gate verification instead of current-password challenge + +### Attribute Value Decision node +Evaluates whether a specific attribute in the user's profile matches a configured comparison operation. + +**Configuration:** + +| Field | Notes | +|---|---| +| `comparisonOperation` | `PRESENT` — checks for attribute existence; `EQUALS` — checks if attribute value equals `comparisonValue` | +| `comparisonAttribute` | The object attribute to compare (e.g., `emailVerified`) | +| `comparisonValue` | The value to compare against when using `EQUALS` operation (e.g., `true`) | +| `identityAttribute` | The attribute used to look up the IDM object (e.g., `userName`) | + +- Outcomes: **True** / **False** +- `PRESENT` is equivalent to a null-check and does not require a `comparisonValue` +- Common use: "Is Email Verified?" gate — `comparisonAttribute: emailVerified`, `comparisonOperation: EQUALS`, `comparisonValue: true`; `False` triggers email verification flow + +### Required Attributes Present node +Checks whether all required attributes are present in shared state before proceeding. + +- Outcomes: **True** (all present) / **False** (one or more missing) +- Use before `CreateObjectNode` in registration flows to prevent partial user creation + +--- + +## User Object Operations + +### Create Object node +Creates a new managed object (typically `managed/alpha_user`) using attributes collected in shared state. + +**Configuration:** + +| Field | Notes | +|---|---| +| `identityResource` | The IDM identity resource to create (e.g., `managed/alpha_user`). Must match the journey's identity resource. | + +- Outcomes: **CREATED** / **FAILURE** +- On `FAILURE`: in production journeys, the failure path runs a `ScriptedDecisionNode` ("Delete User Entry") to clean up any partially created record before routing to FailureNode — prevents orphaned partial accounts +- `CREATED`: route to `IncrementLoginCountNode` to initialize the login count to 1 (enables `LoginCountDecisionNode` to trigger on the subsequent login) + +### Patch Object node +Updates an existing managed object with attribute values from shared state. + +**Configuration:** + +| Field | Notes | +|---|---| +| `identityResource` | The IDM identity resource to patch (e.g., `managed/alpha_user`). Must match the journey's identity resource. | +| `patchAsObject` | `false` (default) — merges changes rather than replacing the whole object | +| `ignoredFields` | Fields from shared state to exclude from the patch (e.g., `[userName]` prevents username modification) | +| `identityAttribute` | Attribute used to look up the object (usually `userName` or `mail`) | + +- Outcomes: **PATCHED** / **FAILURE** +- Uses: password update, email verification status flag, preferences update, MFA device metadata update + +--- + +## Social Login + +See `nodes/federation-contextual-nodes.md` → Social Provider Handler node (V2) and Select Identity Provider node for the full social login node documentation and entry patterns. + +--- + +## Consent and Terms + +### Accept Terms and Conditions node +Presents the current T&C version to the user and records acceptance with timestamp and version. + +- Outcomes: single +- Common placement: embedded as a PageNode child alongside registration fields (co-located with signup form) + +### Terms and Conditions Decision node +Checks whether the user has accepted the current version of terms. + +- Outcomes: **True** (accepted and current) / **False** (not accepted or version outdated) +- `False` → `AcceptTermsAndConditionsNode`; used in authentication journeys to enforce T&C re-acceptance when a new version is published + +### Consent Collector node +Collects user consent for data processing or specific purposes defined in the IDM consent schema. + +**Configuration:** + +| Field | Notes | +|---|---| +| `allRequired` | `true` — all configured mappings require consent to proceed | +| `message` | Localized privacy and consent notice displayed to the user | + +- Outcomes: single + +--- + +## Knowledge-Based Authentication (KBA) + +### KBA Definition node +Prompts the user to define KBA security questions and answers during enrollment. + +**Configuration:** + +| Field | Notes | +|---|---| +| `message` | Localized message describing the purpose (default: "Select a security question.") | +| `allowUserDefinedQuestions` | `true` allows users to write their own custom KBA questions | + +- Outcomes: single (questions stored in user profile) + +### KBA Verification node +Presents the user's pre-defined KBA questions and checks answers. + +**Configuration:** + +| Field | Notes | +|---|---| +| `kbaInfoAttribute` | The attribute in the IDM user object where KBA questions/answers are stored | +| `identityAttribute` | The IDM attribute used to identify the object (e.g., `userName`) | + +- Outcomes: **True** (answers correct) / **False** (answers incorrect) + +### KBA Decision node +Evaluates whether the user has already set up KBA questions (i.e., meets the system's minimum required count). + +**Configuration:** + +| Field | Notes | +|---|---| +| `identityAttribute` | The IDM attribute used to retrieve the object | + +- Outcomes: **True** (KBA set up and meets minimum) / **False** (not set up or insufficient) + +--- + +## Profile Completeness + +### Profile Completeness Decision node +Checks whether the user's profile meets a configured completeness threshold (% of non-null, user-viewable, user-editable fields). + +**Configuration:** + +| Field | Notes | +|---|---| +| `threshold` | Percentage [0–100] of required fields that must be populated | +| `identityAttribute` | The attribute to query for the IDM object | + +- Outcomes: **True** (profile meets threshold) / **False** (profile incomplete) +- Use to trigger progressive profiling after login + +### Query Filter Decision node +Evaluates an LDAP/SCIM-style filter against the user's profile attributes. + +**Configuration:** + +| Field | Notes | +|---|---| +| `queryFilter` | SCIM/LDAP-style filter string evaluated against the user object | +| `identityAttribute` | Attribute used to retrieve the user object | + +- Outcomes: **True** (filter matches) / **False** (filter does not match) +- SCIM filter syntax example: `"!(/preferences pr) or /preferences/marketing eq false or /preferences/updates eq false"` — matches users who are missing the preferences attribute OR have it set to false +- Use dot-path notation for nested attributes: `/preferences/marketing` +- Use in combination with `LoginCountDecisionNode` for progressive profiling double-gate (see `journey-use-cases/progressive-profiling.md`) + +--- + +## User Lifecycle / Provisioning + +### Time Since Decision node +Evaluates whether a specified amount of time has elapsed since the user registered. + +**Configuration:** + +| Field | Notes | +|---|---| +| `elapsedTime` | Elapsed time in minutes to compare against | +| `identityAttribute` | The attribute to query in the IDM object | + +- Outcomes: **True** (elapsed time has passed) / **False** (elapsed time has not passed) +- Use case: gate time-sensitive flows (e.g., require re-verification if account was created more than N minutes ago) + +For passthrough authentication against a third-party system via ICF connector, see `nodes/basic-auth-nodes.md` → Passthrough Authentication node. + +--- + +## PingOne Verify (Identity Proofing) + +### PingOne Verify Evaluation node +Initiates a PingOne Verify identity proofing session (document capture, liveness check). + +- Outcomes: **Success** / **Failure** / **Error** +- Requires PingOne Verify service configured in the environment + +### PingOne Verify Completion Decision node +Checks the result of an ongoing PingOne Verify session. + +- Outcomes: **Pass** / **Fail** / **Pending** + +--- + +## PingOne User Operations + +### PingOne Create User node +Creates a user in PingOne MT from within an AIC journey. + +### PingOne Delete User node +Deletes a PingOne MT user from within an AIC journey. + +### PingOne Identity Match node +Attempts to match an authenticating user against a PingOne identity. + +- Outcomes: **Single Match** / **No Match** / **Multiple Matches** + +--- + +## Common patterns + +| Pattern | Nodes | +|---|---| +| Self-registration | AttributeCollector → RequiredAttributesPresent → CreateObject(CREATED) → IncrementLoginCount → Success | +| Create Object with cleanup | CreateObject(FAILURE) → ScriptedDecision("Delete User Entry") → FailureNode | +| Progressive profiling | ProfileCompletenessDecision(False) → AttributeCollector → PatchObject | +| T&C enforcement | TermsAndConditionsDecision(False) → AcceptTermsAndConditions → Success | +| KBA enrollment at first login | KbaDecision(False) → KbaCreate → Success | +| Anti-enumeration lookup | IdentifyExistingUser(true AND false) → EmailSuspendNode (same successor for both outcomes) | +| Passwordless branch | AttributePresentDecision(`password` = False) → EmailSuspendNode | +| Email verification gate | AttributeValueDecision(`emailVerified` EQUALS `true` = False) → email verification inner journey | +| Social + local choice | SelectIdPNode(socialAuthentication) → SocialProviderHandlerV2 / (localAuthentication) → Platform Username path | +| Passthrough auth (migration) | CredentialCollection → PassthroughAuthenticationNode(Authenticated) → Success / (Failed) → FailureNode | +| Social registration | SocialProviderHandlerV2(NO_ACCOUNT) → PageNode(AttributeCollector) → RequiredAttributesPresent → CreateObject | +| Time-gated re-verification | TimeSinceDecision(True) → verification inner journey | + +## Prerequisites + +- Managed object schema configured in IDM with the required attributes present and viewable. +- IDM accessible from the AIC/PingAM journey engine (standard in AIC; requires IDM connector in standalone PingAM). + +## Common variants + +- **B2B tenant:** use `managed/alpha_organization` alongside `managed/alpha_user` for org-scoped attribute collection. +- **Social registration:** Social Provider Handler V2 writes normalized claims to shared state; `AttributeCollectorNode` fills any missing required fields before `CreateObjectNode`. + +## Related references + +- `nodes/basic-auth-nodes.md` +- `nodes/federation-contextual-nodes.md` +- `nodes/utility-nodes.md` +- `journey-use-cases/account-recovery-and-username-reminder.md` +- `journey-use-cases/social-and-local-registration-authentication.md` +- `journey-use-cases/progressive-profiling.md` + +## Source + +[Identity management nodes](https://docs.pingidentity.com/auth-node-ref/latest/overview.html) +[Attribute Collector node](https://docs.pingidentity.com/auth-node-ref/latest/attribute-collector.html) +[Create Object node](https://docs.pingidentity.com/auth-node-ref/latest/create-object.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md new file mode 100644 index 0000000..70958de --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/mfa-nodes.md @@ -0,0 +1,331 @@ +--- +title: "PingOne ST — MFA Nodes" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: ["mfa"] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — MFA Nodes + +Nodes for registering and verifying second factors: WebAuthn/passkeys, OATH/TOTP, push, OTP, and recovery codes. + +## Scope + +**Covers:** All built-in MFA node types, their configuration, outcomes, and composition patterns — including production-observed settings from live journey exports. +**Does NOT cover:** Risk-based MFA step-up routing — see `nodes/risk-management-nodes.md`. PingOne Verify (identity proofing) — see `ping-universal-services`. + +--- + +## WebAuthn / Passkeys + +### WebAuthn Registration node +Registers a FIDO2/WebAuthn authenticator (passkey, security key, platform authenticator) for a user. + +**Production configuration:** + +| Field | Observed value | Notes | +|---|---|---| +| `userVerificationRequirement` | `PREFERRED` | Allows devices without UV but prefers it | +| `authenticatorAttachment` | `UNSPECIFIED` | Platform or cross-platform authenticators accepted | +| `generateRecoveryCodes` | `true` | Always enable; recovery codes displayed immediately after | +| `maxSavedDevices` | `0` | Unlimited registered devices per user | +| `attestationPreference` | `NONE` | No attestation statement required | +| `acceptedSigningAlgorithms` | `[ES256, RS256]` | Covers most device types | +| `timeout` | `60` | Seconds before registration attempt expires | +| `asScript` | `true` | Required for hosted pages; injects WebAuthn as a script | + +- Outcomes: **success** / **unsupported** / **failure** / **error** +- On `success`: route immediately to `RecoveryCodeDisplayNode` before proceeding +- On `unsupported`, `failure`, `error`: route to FailureNode or back to device selection +- Requires HTTPS. Configure Relying Party Identifier (defaults to tenant domain). + +### WebAuthn Authentication node +Authenticates a user with a previously registered FIDO2 device. + +**Production configuration:** + +| Field | Observed value | Notes | +|---|---|---| +| `userVerificationRequirement` | `PREFERRED` | | +| `isRecoveryCodeAllowed` | `true` | Exposes `recoveryCode` outcome | +| `timeout` | `60` | | +| `detectSignCountMismatch` | `false` | Set `true` in high-security deployments to detect cloned authenticators | +| `asScript` | `true` | | + +- Outcomes: **success** / **unsupported** / **noDevice** / **failure** / **error** / **recoveryCode** +- Route `noDevice` to device registration inner journey, not directly to Failure +- Route `recoveryCode` to a dedicated PageNode embedding `RecoveryCodeCollectorDecisionNode` (type: `WEB_AUTHN`) +- Route `No Device Registered` / `noDevice` to the same failure handling as `failure` to prevent username enumeration + +### WebAuthn Device Storage node +Persists the registered WebAuthn device credential to the user's profile. + +- Place after WebAuthn Registration `success` outcome, before RecoveryCodeDisplayNode. + +--- + +## OATH / TOTP + +### OATH Registration node +Registers a TOTP/HOTP authenticator app by generating and displaying a QR code shared secret. + +**Production configuration:** + +| Field | Observed value | Notes | +|---|---|---| +| `algorithm` | `TOTP` | Standard time-based OTP | +| `passwordLength` | `SIX_DIGITS` | | +| `totpTimeInterval` | `30` | Standard 30-second window | +| `addChecksum` | `false` | | +| `generateRecoveryCodes` | `true` | Always enable | +| `minSharedSecretLength` | `32` | | +| `accountName` | `USERNAME` | Displayed in the authenticator app | +| `issuer` | `ForgeRock` or tenant name | Displayed in the authenticator app | +| `bgColor` | `032b75` | QR code background color | + +- Outcomes: single (on success; display OATH device QR code) +- Always preceded by `GetAuthenticatorAppNode` when the user may not have an authenticator app installed +- On `successOutcome`: route to `RecoveryCodeDisplayNode` + +### Get Authenticator App node +Displays download links for the ForgeRock Authenticator (Play Store + App Store) before OATH or Push registration. Used when `OathTokenVerifierNode` or `PushAuthenticationSenderNode` returns `notRegisteredOutcome` / `NOT_REGISTERED`. + +- Outcomes: single (user has acknowledged; proceed to registration) +- ForgeRock Authenticator URLs: Play Store `com.forgerock.authenticator`, App Store ID `1038442926` + +### OATH Token Verifier node +Verifies the OTP entered by the user against the registered OATH device. + +**Production configuration:** + +| Field | Observed value | Notes | +|---|---|---| +| `algorithm` | `TOTP` | | +| `totpTimeInterval` | `30` | | +| `totpHashAlgorithm` | `HMAC_SHA1` | | +| `totpTimeSteps` | `2` | Clock drift allowance: ±1 window | +| `maximumAllowedClockDrift` | `5` | Minutes | +| `hotpWindowSize` | `100` | For HOTP counter drift | +| `isRecoveryCodeAllowed` | `true` | Exposes `recoveryCodeOutcome` | + +- Outcomes: **successOutcome** / **failureOutcome** / **notRegisteredOutcome** / **recoveryCodeOutcome** +- `notRegisteredOutcome`: route to `GetAuthenticatorAppNode` → OATH Registration +- `recoveryCodeOutcome`: route to dedicated recovery code PageNode +- Must be embedded as a PageNode child, alongside a `ScriptedDecisionNode` ("Validate Verification Code") for client-side input validation +- On `failureOutcome`: route to `RetryLimitDecisionNode` (see retry-loop-with-lockout pattern below) + +### OATH Device Storage node +Persists the OATH device to the user's profile. Place after OATH Registration before proceeding. + +### HOTP Generator node +Generates an HMAC-based OTP for delivery via a side channel (email script, SMS). + +- `length: 6` — stores OTP in transient state as `oneTimePassword` +- Outcomes: single +- Used in email-delivered OTP flows where `OTPEmailSenderNode` is not used (e.g., when delivery is handled by a custom `ScriptedDecisionNode` calling IDM `openidm.action()`) + +--- + +## Push Authentication + +### Push Registration node +Registers the user's mobile device for push-based authentication. + +- Outcomes: **successOutcome** / **failureOutcome** / **timeoutOutcome** +- On `successOutcome`: route to `RecoveryCodeDisplayNode` +- Always preceded by `GetAuthenticatorAppNode` when device may not be enrolled + +### Push Sender node +Sends a push notification to the user's registered device. + +**Production configuration:** + +| Field | Observed value | Notes | +|---|---|---| +| `pushType` | `DEFAULT` | | +| `mandatory` | `true` | Requires a response | +| `messageTimeout` | `120000` | Milliseconds (2 minutes) | +| `captureFailure` | `false` | | +| `contextInfo` | `false` | | + +- Outcomes: **SENT** / **NOT_REGISTERED** +- `NOT_REGISTERED`: route to `GetAuthenticatorAppNode` → Push Registration + +### Push Wait node +Polls for the result of a sent push notification. + +- `secondsToWait: 5` +- Outcomes: **DONE** (polling complete; proceed to `PushResultVerifierNode`) / **EXITED** (user chose to enter a code instead) +- `EXITED`: route to an OTP/OATH verification PageNode as a fallback when the user dismisses the push wait screen + +### Push Result Verifier node +Verifies the final push authentication result. + +- Outcomes: **TRUE** (approved) / **FALSE** (denied) / **WAITING** (not yet responded) / **EXPIRED** (timed out) +- `WAITING`: loop back through `PushWaitNode` → `DONE` → return to PushResultVerifierNode +- `EXPIRED`: route to `RetryLimitDecisionNode` → `Retry` → re-send push; `Reject` → FailureNode +- `FALSE` (denied): route directly to FailureNode + +--- + +## OTP via Email / SMS + +### OTP Email Sender node +Generates a one-time password and sends it to the user's registered email address. + +- Outcomes: single (OTP stored in transient state) + +### OTP SMS Sender node (`OneTimePasswordSmsSenderNode`) +Generates and sends an OTP to the user's phone via SMS. + +- Outcomes: single +- Used in phone-based MFA device registration (phone-number verification before enrollment) + +### OTP Collector Decision node +Collects and validates the OTP entered by the user. + +- `passwordExpiryTime: 5` (minutes) +- Outcomes: **True** (OTP valid) / **False** (OTP invalid or expired) +- On `False`: route to `RetryLimitDecisionNode` (see retry-loop-with-lockout pattern) + +> **PageNode required:** `OTP Collector Decision` must always be a PageNode child. A standalone instance renders no input field. Always embed it in a PageNode with a `ScriptedDecisionNode` for input validation (see `nodes/utility-nodes.md` → PageNode Rule 3). + +--- + +## Recovery Codes + +### Recovery Code Display node +Generates and displays recovery codes to the user immediately after MFA enrollment. Codes are stored in the user's profile. + +- Outcomes: single +- **Always place immediately after the MFA registration node's success outcome** — before any other step. If the user exits without seeing the codes, they lose access to them. + +### Recovery Code Collector Decision node +Prompts for and validates a recovery code as an alternative to the primary MFA factor. + +- `recoveryCodeType`: set to the MFA method — `OATH` / `WEB_AUTHN` / `PUSH` +- Outcomes: **True** (code valid) / **False** (code invalid) +- Must be a PageNode child alongside a `ScriptedDecisionNode` ("Validate Recovery Code") for client-side input validation + +--- + +## Recovery Code Architecture + +The following pattern appears consistently across all production MFA journeys: + +``` +Registration: + [MFA Registration node] (generateRecoveryCodes: true) + → successOutcome → RecoveryCodeDisplayNode → proceed + +Authentication: + [MFA Verifier node] (isRecoveryCodeAllowed: true) + → recoveryCodeOutcome → PageNode(RecoveryCodeCollectorDecisionNode, ScriptedDecisionNode) + → True → Success + → False → RetryLimitDecisionNode +``` + +Each `RecoveryCodeCollectorDecisionNode` must be typed to the MFA method via `recoveryCodeType`. Mixing types (e.g., using an OATH-typed collector with a WebAuthn authentication flow) causes validation failures. + +--- + +## Combined Registration + +### Combined MFA Registration node +Registers both OATH (TOTP) and Push simultaneously in a single step. + +**Production configuration:** `algorithm: TOTP`, `passwordLength: SIX_DIGITS`, `totpTimeInterval: 30`, `generateRecoveryCodes: true` + +- Outcomes: **successOutcome** / **failureOutcome** / **timeoutOutcome** +- `timeoutOutcome`: route to `RetryLimitDecisionNode` for retry +- On `successOutcome`: route to `RecoveryCodeDisplayNode` +- Use when the device selection PageNode offers an `OATH+PUSH` combined option + +### MFA Registration Options node +Allows users to select which MFA factors to register. + +### Enable Device Management node +Enables device management capabilities for subsequent device binding nodes. + +--- + +## Device Binding + +### Device Binding node +Binds the current device to the authenticated user using a device-specific key. + +- Outcomes: **Success** / **Failure** / **Unsupported** + +### Device Binding Storage node +Persists device binding data to the user's profile. + +### Device Signing Verifier node +Verifies a device signature created by a previously bound device. + +- Outcomes: **Success** / **Failure** / **No Device Bound** + +--- + +## Retry-Loop-With-Lockout Pattern + +Every MFA input path in production journeys uses a consistent retry loop: + +``` +[Input PageNode] + → failure outcome + → RetryLimitDecisionNode (retryLimit: 3, incrementUserAttributeOnFailure: true) + → Retry → ScriptedDecisionNode ("Set Invalid Code Error Message") + → [Input PageNode] (reads invalidCodeErrorMessage from shared state via callbacksBuilder.textOutputCallback) + → Reject → FailureNode +``` + +The `ScriptedDecisionNode` sets `invalidCodeErrorMessage` in shared state. The PageNode script reads it and displays the error inline on the next render without an extra round-trip. This pattern applies to OATH, OTP, recovery code, and Push-expired retry flows. + +--- + +## Common patterns + +| Pattern | Nodes | +|---|---| +| WebAuthn login | WebAuthn Authentication → (noDevice) → WebAuthn Registration | +| TOTP registration | GetAuthenticatorAppNode → OATH Registration → OATH Device Storage → Recovery Code Display | +| TOTP authentication | PageNode(OathTokenVerifierNode + ScriptedDecisionNode) → (successOutcome) Success | +| OTP email step-up | OTP Email Sender → PageNode(OTP Collector Decision) → (True) Success | +| Push authentication | Push Sender → (SENT) Push Wait → (DONE) Push Result Verifier → (TRUE) Success | +| Push with EXITED fallback | Push Wait(EXITED) → PageNode(OathTokenVerifierNode) → retry loop | +| Combined MFA enrollment | Combined MFA Registration → Recovery Code Display → Success | +| Recovery code fallback (OATH) | OathTokenVerifierNode(recoveryCodeOutcome) → PageNode(RecoveryCodeCollectorDecisionNode[OATH]) → (True) Success | + +## Prerequisites + +- FIDO2/WebAuthn registration and authentication require HTTPS and a configured Relying Party. +- OATH/TOTP requires the ForgeRock Authenticator or a compatible TOTP app on the user's device. +- Push authentication requires the ForgeRock Authenticator app and a configured push service (APNs/FCM). +- Twilio SMS/voice OTP requires a Twilio account SID, auth token, and Verify service SID. + +## Common variants + +- **FIDO2 enterprise deployment:** set `userVerificationRequirement: REQUIRED` and `authenticatorAttachment: CROSS_PLATFORM` for security key policies. +- **SMS-only fallback:** use `OTPSMSSenderNode` + `OTPCollectorDecisionNode` when device-based MFA is not available. + +## Related references + +- `nodes/basic-auth-nodes.md` +- `nodes/risk-management-nodes.md` +- `nodes/utility-nodes.md` +- `journey-use-cases/mfa-authentication-multi-method.md` +- `journey-use-cases/passwordless-mfa-registration.md` + +## Source + +[MFA nodes overview](https://docs.pingidentity.com/auth-node-ref/latest/overview.html) +[WebAuthn Authentication node](https://docs.pingidentity.com/auth-node-ref/latest/webauthn-authentication.html) +[OATH nodes](https://docs.pingidentity.com/auth-node-ref/latest/oath-registration.html) diff --git a/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/node-fundamentals.md b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/node-fundamentals.md new file mode 100644 index 0000000..dc6214b --- /dev/null +++ b/plugins/ping-identity/skills/ping-orchestration/references/curated/pingone-st/nodes/node-fundamentals.md @@ -0,0 +1,118 @@ +--- +title: "PingOne ST — Node Fundamentals" +product_family: pingone-st +products: ["pingone-aic", "pingam"] +capabilities: ["orchestration"] +services: [] +audience: ["developer", "architect"] +use_cases: ["workforce", "customer"] +doc_type: reference +status: current +canonical: true +last_updated: "2026-06-02" +slug: "https://docs.pingidentity.com/auth-node-ref/latest/overview.html" +--- + +# PingOne ST — Node Fundamentals + +Tribal knowledge and non-obvious invariants about how nodes behave in PingOne ST journeys. Rules here are validated from live AIC sessions — they are not documented clearly in official docs and are common sources of bugs. + +## Scope + +**Covers:** Node composition rules, PageNode usage, child node requirements, outcome wiring, and known gotchas. +**Does NOT cover:** Individual node reference — see the specific node files (e.g., `basic-auth-nodes.md`, `mfa-nodes.md`, `utility-nodes.md`). + +--- + +## PageNode rules + +### PageNode is never used alone + +A PageNode without child nodes renders as an empty page ("Drag nodes here") — no inputs, no outcomes, no way for the user to proceed. PageNodes only work when child nodes are declared inside `config.nodes`. + +This is the most common PageNode bug in `createJourney` calls: the PageNode is created but its children are omitted or passed as an empty array. + +### Child nodes must be declared in `config.nodes` + +When building a journey via the API or `createJourney`, child nodes of a PageNode must be listed in the `config.nodes` array. Each entry requires at minimum: + +```json +{"nodeType": "", "displayName": "