feat!: delegated IdP, identity chaining, and capability-driven scope model#330
feat!: delegated IdP, identity chaining, and capability-driven scope model#330
Conversation
…model
Redesign identity linking for multi-merchant agentic commerce.
Businesses can offer delegated identity providers, allowing buyers to
authenticate through a trusted IdP (e.g., Google, Shop) instead of
creating per-merchant accounts. Platforms can support these IdPs to
streamline identity linking — authenticate once, reuse across every
business that trusts the same provider.
## Delegated Identity Providers
Businesses declare trusted identity providers in config.providers,
keyed by reverse-domain identifier. Each provider specifies an auth_url
for metadata discovery (RFC 8414, OIDC fallback on 404). A business MAY
list itself as a provider, unifying business-hosted OAuth with delegated
auth under the same discovery mechanism.
## Identity Chaining (Accelerated IdP Flow)
When a platform already holds an IdP token, it chains the buyer's
identity to a new business without a browser redirect, per
draft-ietf-oauth-identity-chaining-08:
1. Platform requests a JWT authorization grant from the IdP (RFC 8693)
using the resource parameter
2. Platform presents the grant to the business (RFC 7523 jwt-bearer)
3. Business validates, resolves buyer identity, issues its own token
The IdP controls consent — it MUST NOT issue grants for businesses the
buyer has not authorized. Businesses MAY auto-provision accounts or
return a continue_url for buyer onboarding. Refresh tokens SHOULD NOT
be issued per the chaining draft §5.4. Two independent token lifecycles
with independent revocation.
## Capability-Driven Scope Model
Scopes define the permissions an identified buyer grants to a platform.
Declarations live centrally on the identity linking config, not on
individual capability schemas:
- config.capabilities map keyed by capability name declares which
capabilities offer buyer-scoped features
- required: true signals buyer identity is mandatory (identity_required
UCP business error if absent, with continue_url for onboarding)
- Sub-scopes use colon separator (e.g. dev.ucp.shopping.order:read),
defined by each capability spec as operation groups
- Scope tokens use capability names directly — no separate namespace
- Capabilities are never pruned from the intersection
- Platforms should request scopes incrementally
Three access levels: public (no auth), agent-authenticated (platform
credentials), buyer-authenticated (identity linking upgrades or gates
access depending on the business's required flag).
## Security
- PKCE MUST (platforms and businesses)
- iss validation MUST (RFC 9207, both sides)
- Exact redirect_uri matching MUST (businesses)
- scopes_supported MUST in RFC 8414 metadata
- 2-step discovery: RFC 8414 primary, OIDC fallback on 404 only
- JWT grants: 60s lifetime, jti single-use enforcement
- Strict abort on non-404 discovery errors
|
Thanks @igrigorik. Agree with the changes. Great addition to UCP! |
| * Before initiating identity chaining with a business, the platform | ||
| **SHOULD** offer the buyer a choice of available identity providers and | ||
| indicate that the selected provider's identity will be shared with the | ||
| business. |
There was a problem hiding this comment.
Is SHOULD the right keyword here if there is only one in-common provider, which would effectively be a no-op?
Also worth considering that a platform may have a preferred IdP (or operate its own), which isn't just a UX convenience, it has potential security advantages around revocation, since a platform controlling its IdP can enforce consistent token revocation across all trusting businesses. That said, the intent to preserve buyer choice and visibility over which identity is shared is an important counterbalance.
| business. | ||
| * Revocation and security events | ||
| * **SHOULD** revoke tokens at both layers when a buyer initiates an | ||
| unlink action (see [Token Lifecycle](#token-lifecycle)). |
There was a problem hiding this comment.
Consider splitting the platform's revocation obligation into two levels:
- Revoking the business-issued token should be a MUST as that's the direct relationship the buyer explicitly asked to sever.
- Revoking the IdP token is a broader action that would cascade across all business relationships established through that IdP, so MAY feels more appropriate, leaving the platform to determine the right blast radius.
| * **MUST** implement standard Token Revocation as defined in | ||
| [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009){ target="_blank" }. | ||
| * **MUST** revoke the specified token and **SHOULD** recursively revoke | ||
| all associated tokens (e.g., revoking a `refresh_token` **MUST** also |
There was a problem hiding this comment.
The example here promotes the SHOULD guidance into a MUST. Should revocation really be optional?
| automatically propagate to the other. | ||
|
|
||
| **Business-issued tokens.** Access tokens issued by the business follow the | ||
| business's token lifecycle. Businesses **SHOULD NOT** issue refresh tokens |
There was a problem hiding this comment.
Architecturally, discouraging refresh tokens for the JWT bearer grant flow makes a lot of sense as it keeps the IdP as the ongoing trust anchor. But that makes the access token lifetime the effective trust re-verification interval, should we be explicit that it should be minutes not hours?
|
|
||
| When multiple identity providers are available, the platform **SHOULD** offer | ||
| the buyer a choice of providers. Before initiating identity chaining, the | ||
| platform **SHOULD** inform the buyer which business will receive their |
There was a problem hiding this comment.
I'm left wondering if strengthening this obligation would be a buyer-centric privacy improvement. The IdP's consent gate covers who and where: it knows the buyer and the target business, and actively gates that relationship. But the fine-grained scope negotiation happens between the platform and the business, outside the IdP's visibility. That makes the platform the only participant that sees both sides: which business receives the identity and what scopes that identity unlocks. The buyer should have visibility into both, not just where their identity is going, but what it will be used for.
| } | ||
| }, | ||
| "additionalProperties": true | ||
| "additionalProperties": false |
There was a problem hiding this comment.
Is there a reason we're closing off extensibility here and leaving it open on provider?
| ] | ||
| }, | ||
| "additionalProperties": true | ||
| "business_schema": { |
There was a problem hiding this comment.
Nit: Should config be required here as it was previously? The old schema required it, and declaring identity linking with no config is effectively a no-op: no providers, no capability scope requirements. It feels like a state that's more likely to be a misconfiguration than an intentional choice. That said, there's an argument for allowing it as an incremental rollout path, where a business signals infrastructure support before capabilities opt in.
| "revocation_endpoint": "https://example.com/oauth2/revoke", | ||
| "scopes_supported": [ | ||
| "dev.ucp.shopping.scopes.checkout_session" | ||
| "ucp:scopes:checkout_session", |
There was a problem hiding this comment.
Nit: This scope format doesn't match the new scope naming convention defined in the Scope Naming section.
| `urn:ietf:params:oauth:grant-type:jwt-bearer` in | ||
| `grant_types_supported` in its | ||
| [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414){ target="_blank" } metadata. | ||
| * **MUST** provide an account creation flow if the buyer does not already have |
There was a problem hiding this comment.
Thoughts on moving this to MAY? I'm thinking of the scenario: "as a Business, I want to automatically sign in existing users, but don't want to interrupt the flow and force users to sign up."
|
The delegated IdP architecture is a big improvement — especially the identity chaining flow for reducing N-merchant handoffs in agentic commerce. One question on provider extensibility, building on @gsmith85's point: the Concrete example: wallet attestation. An agent presents a signed credential proving it meets specific conditions (holds assets, passes compliance checks). The verifier fetches the issuer's JWKS from This maps to the "agent-authenticated" access level in #330's three-tier model — the agent proves something about itself without a buyer-identity OAuth flow. Would it make sense for |
|
Really like the direction here, the delegated IdP model and identity chaining solve a real pain point for multi-merchant agentic commerce. We've been working through this exact problem on our end and this is a big step forward. One thing we keep coming back to is per-business buyer consent in the Accelerated IdP Flow. With the traditional Account Linking flow, the buyer sees the business's consent screen and explicitly approves. The Accelerated IdP Flow removes that touchpoint - identity chains to new businesses server-to-server, which is great for UX but means the buyer never explicitly consents to (or may not even be aware of) each individual business receiving their identity. The spec has the right instincts here - the MUST NOT on IdPs issuing grants for unauthorized businesses, and the SHOULD on platforms informing the buyer, but neither defines a concrete mechanism. And the underlying A few things we're thinking about:
|
Brings forward the delegated identity provider design from #330 into this PR's OAuth 2.0 foundation. The core capability-driven scope model and security posture from #354 are unchanged — this commit adds the multi-merchant identity layer on top. ## Added ### Delegated Identity Providers (`config.providers`) Businesses can declare trusted external OAuth identity providers in `config.providers`, keyed by reverse-domain identifier. A business MAY also list itself as a provider, unifying business-hosted OAuth and delegated IdP under the same discovery mechanism. When `config.providers` is absent, platforms fall back to RFC 8414 discovery on the business domain — preserving the baseline behavior already specified in this PR. ### Identity Chaining (Accelerated IdP Flow) When a platform already holds a valid IdP token and encounters a new business that trusts the same IdP, it can chain the buyer's identity without a browser redirect. Implements draft-ietf-oauth-identity-chaining-08: 1. Platform obtains a JWT authorization grant from the IdP via token exchange (RFC 8693, `resource` parameter identifies target business) 2. Platform presents the JWT grant to the business via JWT bearer assertion (RFC 7523) 3. Business validates, resolves buyer identity, issues its own token This solves the N-merchant = N-OAuth-handoff problem for agentic commerce. ### Supporting sections - **Account Linking**: one-time OAuth flow between platform and IdP, reusable across businesses that trust the same provider - **Headless and Agentic Contexts**: RFC 8628 device authorization for CLI agents and voice assistants - **JWT Authorization Grant**: claims table (iss, sub, aud, exp, iat, jti), 60s lifetime recommendation, single-use enforcement fail-closed on JWKS retrieval failure - **Token Lifecycle**: dual-layer management — business tokens and IdP tokens have independent lifecycles and revocation; businesses SHOULD NOT issue refresh tokens on JWT bearer grants - **IdP Requirements**: metadata requirements (revocation_endpoint, jwks_uri, token-exchange grant type), token exchange processing rules - **Buyer Awareness**: provider choice UX, consent disclosure - **Chaining Error Handling**: error table mapping JWT validation failures to standard OAuth error responses ## Changed - **Overview**: removed "v1 auth mechanism" hedging; identity chaining is part of the spec, not a future extension - **Participants table**: added Identity Provider (IdP) role - **General Guidelines — Platforms**: added provider selection and chaining disclosure guidance - **General Guidelines — Businesses**: added JWT bearer assertion MUST when `config.providers` is present - **Scopes**: added "Scopes and External Identity Providers" subsection clarifying that UCP scopes are requested from the business, not the IdP - **Security Considerations**: added JWT grant lifetime, jti single-use, and grant replay items - **Future Extensibility**: removed `config.providers` subsection (now normative); only `config.mechanisms` remains as future work - **Auth server metadata example**: added jwt-bearer grant type, explanatory note - **Business Profile example**: added providers map, fixed `required` → `auth_required` to match schema field name - **overview.md**: added providers to business profile example ### Schema - Added `provider` $def (object with `auth_url` URI) - Added `providers` property to business config (optional, map keyed by reverse-domain) - Restructured $defs to nest platform_schema/business_schema under `dev.ucp.common.identity_linking`, required by the composition algorithm - Removed `"version": "Working Draft"` from schema top-level - Updated $comment to reflect providers as shipped (not reserved)
Redesign identity linking for multi-merchant agentic commerce. Businesses can offer delegated identity providers, allowing buyers to authenticate through a trusted IdP (e.g., Google, Shop) instead of creating a per-merchant account. Platforms can support these IdPs to streamline identity linking — authenticate once, reuse across businesses that trusts the same provider.
This PR carries builds on / supersedes #265, reverted in #329.
Delegated Identity Providers
The current identity linking spec assumes 1:1 — each business operates its own OAuth authorization server, requiring a full OAuth dance per merchant. In agentic commerce, where platforms shop across many businesses on a buyer's behalf, this means N merchants = N handoffs.
Businesses can now declare trusted identity providers in
config.providers, keyed by reverse-domain identifier:Here the business trusts Google as an external IdP and also lists itself (
com.example.merchant) for business-hosted OAuth — both use the same discovery mechanism. Each provider'sauth_urlis resolved via RFC 8414, with OIDC/.well-known/openid-configurationfallback on 404.Two distinct flows:
Identity chaining
The accelerated flow implements a two-phase pattern:
resourceparameter to identify the target businessThe IdP controls consent — it MUST NOT issue grants for businesses the buyer has not authorized. Businesses MAY auto-provision accounts or return a
continue_urlfor buyer onboarding (terms acceptance, etc.). Two independent token lifecycles with independent revocation. Refresh tokens SHOULD NOT be issued per the chaining draft.Capability-driven scope model
Scopes define the permissions an identified buyer grants to a platform within a capability. Unlike #265's approach of annotating individual capability schemas with
identity_scopes, scope declarations live centrally on the identity linking config. This is because whether a capability requires buyer auth is a business decision — a B2B wholesaler gates catalog access, a B2C retailer serves it publicly.Three access levels
Identity linking upgrades capabilities to buyer-authenticated access. Capabilities are never pruned from the intersection based on identity linking; there is no capability pruning algorithm.
Config shape
Building on the provider example, businesses declare which capabilities offer buyer-scoped features alongside their trusted IdPs:
required: accessible without buyer auth, identity upgrades the experiencerequired: true: business returnsidentity_requirederror without buyer identityscopes: sub-scope operation groups defined by each capability's spec, joined with colon on the wire (e.g.,dev.ucp.shopping.order:read)Carried forward from #265
issvalidation MUST (RFC 9207, both sides)redirect_urimatching MUST (businesses)scopes_supportedMUST in RFC 8414 metadatajtisingle-use enforcementType of change
functionality to not work as expected, including removal of schema files
or fields)
!to my PR title (e.g.,feat!: remove field).Checklist