feat!: identity linking OAuth 2.0 foundation with capability-driven scopes#354
Conversation
- Replace "consumer surfaces/platforms" with "consumer platforms" and "businesses" with "business platforms" for consistency. - Enhance the definitions of consumer and business platforms, emphasizing their roles in capability consumption and exposure. - Revise key goals and responsibilities to reflect updated terminology and clarify the interaction dynamics within the UCP framework. - Introduce a new section on capabilities, detailing their structure and examples to improve understanding of UCP's functionality.
- Clarified the role of Payment & Credential Providers to emphasize the secure handling of sensitive user data. - Enhanced the description of Agentic Commerce to include various modalities for AI agents. - Revised terminology for distinct actors in the UCP framework to improve clarity. - Updated capability negotiation process to specify version selection and mutual agreement. - Improved examples and descriptions for capabilities and transport bindings to align with current standards.
- Updated terminology to replace "consumer platforms" with "clients" and "business platforms" with "providers" for consistency and clarity. - Enhanced descriptions of the roles and responsibilities of clients and providers in the UCP framework. - Revised key goals and capabilities to reflect the updated terminology and improve understanding of UCP's functionality.
- Replaced "Client" with "Platform" and "Provider" with "Business" for consistency.
- Updated the identity linking specification to clarify the role of platforms and businesses in buyer-authenticated commerce experiences. - Introduced a new JSON schema for identity linking, detailing the configuration for capabilities that require buyer identity. - Revised the overview and general guidelines sections to reflect the updated terminology and structure for identity linking capabilities. - Added new error code for identity requirements in the shopping types schema.
Restores docs/documentation/core-concepts.md to match Universal-Commerce-Protocol/ucp upstream main. The local changes belong to a separate PR and should not be included here.
|
Thanks for laying the extensibility groundwork explicitly. The reserved Once this lands, we can follow up with a concrete Happy to wait for this PR to merge before opening the follow-up so we're not creating extension-point churn. |
- Renamed the `required` field to `auth_required` in the identity linking specification and JSON schema to enhance clarity regarding buyer identity requirements.
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)
igrigorik
left a comment
There was a problem hiding this comment.
@amithanda nice work on this, lots of good improvements!
We need to land support for delegated IdPs. This is a key and painfully missing feature, which we proposed the shape for in #330. Comparing the two branches, we need to reconcile and merge, and I think it's easier to build on what you've scoped here.
I took a run at bringing over additional logic from #330 in 603f4c8. Because your PR is against your own fork I can't open a stacked PR. That said, if the commit looks good, I can push it into your branch directly and we can continue iterating against your current branch and close out #330. PTAL, any objections?
With above in place, I think we can collapse providers vs mechanisms into single primitive. They are both attempting to answer the same question: "who does the business trust to vouch for buyers, and what proof protocol do they speak?"
An OAuth IdP and a wallet attestor both have:
- A reverse-domain trust anchor (com.google, com.example.attestor)
- A discovery mechanism (auth_url → RFC 8414, provider_jwks → JWKS endpoint)
- A proof format (OAuth tokens, ES256 signed payloads)
- An explicit trust relationship (business lists them)
Keeping them as separate keys creates ambiguity: are mechanisms alternatives to OAuth (platform picks one) or complements (identity via OAuth + wallet proof for eligibility)? The TLS cipher-suite negotiation analogy on mechanisms implies the former, but your actual use case in #264/#280 looks more like the latter.
Proposed reconciliation
Unify under providers with a type discriminator. Default type is oauth2 when absent — zero wire-format change for existing entries:
"config": {
"providers": {
"com.google": {
"auth_url": "https://accounts.google.com/"
},
"com.example.merchant": {
"auth_url": "https://merchant.example.com/"
},
"com.example.attestor": {
"type": "wallet_attestation",
"provider_jwks": "https://attestor.example.com/.well-known/jwks.json",
...
}
},
"capabilities": { ... }
}What this gets us:
- Single trust surface. Business declares all trusted identity/attestation sources in one map.
- Clean forward-compat. Platforms skip provider entries whose
typethey don't recognize. No separate "ignore unknown config fields" rule for a sibling key.
What we drop: The config.mechanisms reserved extension point. It's replaced by new type values on provider entries, which is a strictly simpler extension model.
The relationship between providers then becomes clear from context: a platform that finds both type: "oauth2" and type: "wallet_attestation" entries knows these are different proof protocols. Whether they're alternatives or complements depends on how config.capabilities references them — that's the follow-up design question for when wallet attestation lands, not something we need to over-specify now.
Four targeted fixes to prepare this PR for backport to 04/08 and
clean stacking of the delegated IdP follow-up.
## 1. Nest $defs under capability name (convention alignment)
Restructures the schema to match the established pattern required by
the composition algorithm: `ext_schema["$defs"][root.name]`
Before:
$defs:
After:
$defs:
capability_identity_config
dev.ucp.common.identity_linking:
platform_schema, business_schema
Why: capability-scoped schemas live under the capability's reverse-domain
name so future tooling can resolve them predictably as
`schema#/$defs/{capability-name}/business_schema`.
## 2. Fix `required` → `auth_required` in overview.md
The business profile example in `overview.md` used `"required": true`
while the schema and spec text use `"auth_required"`. Anyone copying the
overview example would hit a validation error.
## 3. Remove top-level `version` field from schema
No other capability schema in the repo carries a top-level `version`
field — version lives on the capability entry in the UCP profile, not
on the schema file itself. Removed for consistency with `checkout.json`,
`fulfillment.json`, `cart.json`, etc.
## 4. Tighten `iss` validation language
Removed the "if present" hedge in two places (For Platforms bullet and
Account Linking Flow step 3). Since the spec requires businesses to
MUST return `iss` in every authorization response, the hedge was
unnecessary and could be read as making `iss` validation conditional.
## 5. $comment updated to reflect unified providers model
The schema-level `$comment` previously described `providers` and
`mechanisms` as two separate reserved extension points. Updated to
describe a single `providers` map with a `type` discriminator defaulting
to `oauth2` — aligning with feedback on Universal-Commerce-Protocol#354 that these are the same
concept (a trust-anchored identity source with a discovery mechanism
and proof protocol), not separate keys. This is $comment-only — no
schema behavior change — and gives the follow-up IdP PR a clean model
to add `providers` onto without rewriting the Future Extensibility
section.
|
One gotcha that's bothering me with scopes as flat array of strings... "dev.ucp.shopping.order": {
"auth_required": true,
"scopes": ["read", "manage"]
}With above contract every scope inherits uniform requirements from a single B2C retailer
B2B wholesaler
Same capability ( Rec: scope level policies + explicit scopes (no bare capability scope)Each listed capability declares its scopes explicitly. Each scope is an object. Wire format is always "dev.ucp.common.identity_linking": [{
"config": {
"providers": { ... },
"capabilities": {
"dev.ucp.shopping.catalog": {
"scopes": {
"search": { "auth_required": false },
"vip": { "auth_required": true }
}
},
"dev.ucp.shopping.order": {
"scopes": {
"read": { "auth_required": true },
"manage": { "auth_required": true, "min_acr": "urn:tl:TL3" }
},
"some_future_foo": "bar"
}
}
}
}]The contract is:
What this unlocks
Alt: flat
|
Added scopes for other capabilities. PTAL |
igrigorik
left a comment
There was a problem hiding this comment.
Hmm, UCP-Identity-Token doesn't feel right. Let's step back...
In standard OAuth, client authentication happens at the /token endpoint (via client_secret_basic, private_key_jwt, mTLS, etc), not via a Bearer header at API time. Authorization at API time uses the access token that carries client_id claim. The user-authorized token IS the platform's presented credential to the resource server -- resource servers validate client_id/azp against expected values.
All that to say, we don't need to stack a "platform Bearer" and a "user Bearer." The collision Maxime flagged only appears because we listed OAuth Bearer as one of the platform auth options at API time -- which, in retrospect, I think is a mistake. OAuth Bearer at API time is the user-authorized token; it's not a separate platform credential.
I think we can unwind and fix this in this PR...
- Drop OAuth Bearer from the platform auth options in
cart-rest.md(line 540) andcheckout-rest.md(line 1408). Platform identity at API time = HTTP Message Signatures (RFC 9421), mTLS, or API key. Authorization: Bearer <user_token>unambiguously carries the user-authorized access token. Add a forward reference from those auth sections toidentity-linking.mdfor scope vocabulary and validation requirements.- Drop
UCP-Identity-Token
@maximenajim please sanity check!
|
Thanks for the feedback. @igrigorik - I agree that we should avoid stacking a 'platform Bearer' and a 'user Bearer' and eliminate the need for a custom However, rather than removing OAuth 2.0 completely from the list of options (which might imply UCP REST bindings don't support standard OAuth at all), I have updated the language in both
Why this modified language is better than removing the option:
Let me know what you think? |
The MUST on client_secret_basic excluded native, desktop, and on-device
agent runtimes (RFC 8252 §8.5 — public clients cannot keep a client_secret)
and also blocked stronger asymmetric methods (private_key_jwt RFC 7523,
tls_client_auth RFC 8705). The IdP support we want to land next requires
asymmetric crypto for JWT bearer assertions.
Replaces the single-method MUST with RFC-8414-driven negotiation:
- Confidential clients SHOULD prefer asymmetric methods; MAY use
client_secret_basic.
- Public clients (RFC 8252 §8.5) MUST use 'none' and rely on PKCE
with S256 as proof-of-possession; MUST NOT embed a client_secret.
- Businesses declare methods in token_endpoint_auth_methods_supported;
SHOULD support an asymmetric method; MAY support 'none' for public
clients. PKCE S256 required when 'none' is advertised.
- Distinct error codes: invalid_client for auth-method failures,
invalid_grant for PKCE failures.
Per TC discussion default access is a merchant policy decision, not a
spec mandate. UCP defines well-known scopes; merchants decide what auth
is required for non-scoped operations.
- Remove default-access framing from capability specs. Each section
now states only the well-known scopes the capability defines.
- Tighten scope descriptions to a consistent shape:
"<operations gated> — <data or behavior unlocked>".
- Hoist protocol-level rules (declaration, derivation, well-known vs
custom extension) into identity-linking.md, where they live once.
Capability specs link to that section instead of duplicating.
- Switch the identity-linking.md B2B walkthrough from
dev.ucp.shopping.checkout:create to :manage. The well-known scope
fits the "no guest checkout" narrative more cleanly (gates all
checkout ops, not just the entry point).
|
Per the TC sync, we converged on a split of responsibilities between UCP and OAuth (RFC8414) for expressing auth requirements vs availability vs runtime hints. As a think-out-loud sanity-check exercise, this means...
Example walkthroughStep 1: discover capabilitiesAgent fetches the UCP profile: {
"ucp": {
"capabilities": {
"dev.ucp.shopping.catalog.search": [{ ... }],
"dev.ucp.shopping.checkout": [{ ... }],
"dev.ucp.common.identity_linking": [{
"config": {
"scopes": {
"dev.ucp.shopping.checkout:manage": {}
}
}
}]
}
}
}Agent learns:
Step 2: discover auth machineryThe agent fetches the OAuth Authorization Server metadata: {
"issuer": "https://merchant.example.com",
"authorization_endpoint": "...",
"token_endpoint": "...",
"jwks_uri": "...",
"scopes_supported": [
"dev.ucp.shopping.checkout:manage",
"dev.ucp.shopping.catalog.search:read"
],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["private_key_jwt", "none"],
...
}Agent learns:
Step 3: reason about the auth landscapeThree sets, distinct semantics:
Step N: runtime contextual hintsStatic metadata answers "is auth available?" but not "is auth valuable in this request?" That's contextual to the merchant and their implementation (persisted carts and wishlists; loyalty tiers; saved address and prefs). The merchant signals this via an informational message in the operation response, using a well-known code: {
...
"messages": [
{
"type": "info",
"code": "identity_optional",
"content": "Sign in for member pricing and personalized results."
}
]
}The agent can present this as an opportunity to the user, and if accepted, it uses the defined machinery and dance of identity linking to negotiate appropriate scopes by introspecting the two discovery documents and relevant descriptions. As a reminder, info messages are non-blocking for related operations (can be ignored by agent). p.s. |
|
@amithanda separate from above, pushed two updates, please sanity check: |
…dentity_linking.json schema.
|
@igrigorik - thank you - both your updates look good. I added the change to add optional description for required scopes. Please feel free to approve, if everything looks good. |
* `scope_policy.description`: $ref shared `description.json` type for
multi-format text (plain/markdown/html). Cross-domain ref to
shopping/types is the minimal change; promoting the type to
common/types is a follow-up.
* New `## Optional Authentication` section + `identity_optional`
info-severity code. Decoupled from per-scope `description` by
design: identity_optional is a runtime per-request notice;
description is static per-scope context for OAuth consent.
* `insufficient_scope` example fixed: response lists the FULL required
scope set, not the delta (per Amit's TC restatement). Platform
computes the diff and uses incremental authorization to avoid
redundant consent prompts. continue_url wording aligned.
* Schema descriptions: drop "public or agent-authenticated access"
framing. TC consensus is that UCP does not prescribe a default;
merchants decide access policy for non-scoped operations.
* `message_info.json` `code`: register known info codes via JSON
Schema `examples` array AND inline names in the description.
Tooling (autocomplete, codegen) and humans both served.
From the boyscout 'leave it better than you found it' rulebook... - Copy _error pattern as standalone file for _info and _warning - Include info and warning codes in reference docs
Replace the pre-baked OAuth `continue_url` pattern with RFC 6750 §3 WWW-Authenticate challenges, plus RFC 9728 Protected Resource Metadata pointers. Resolves David's TC flag. Why: pre-baking an authorization URL forces the merchant to own parameters it can't sensibly own — PKCE code_challenge, state, redirect_uri, client_id are all client-side concerns. Native/agent clients per RFC 8252 construct their own authorization request anyway, so the pre-baked URL was either ignored or rewritten. Standard OAuth client libraries parse WWW-Authenticate Bearer challenges automatically; custom continue_url parsing was UCP-specific dead weight. * For Platforms: MUST process WWW-Authenticate Bearer challenges per RFC 6750 §3 on 401/403; extract scope parameter; SHOULD follow resource_metadata pointer per RFC 9728. Bumped Bearer Authorization bullet with RFC 6750 §2.1 reference. * For Businesses: MUST emit Bearer challenge on identity_required (401) and insufficient_scope (403). RFC 9728 SHOULD bullet upgraded to reference /.well-known/oauth-protected-resource and integration with WWW-Authenticate. * identity_required: full normative restructure (status code, header, body). realm MUST be issuer URI; error="invalid_token" when token present-but-bad; error SHOULD be omitted when no token (RFC 6750 §3.1). resource_metadata SHOULD. continue_url retained for non-OAuth onboarding flows ONLY; explicit MUST NOT for pre-baked OAuth URLs. * insufficient_scope: full normative restructure. realm + error + scope (full required set, not delta) MUST. resource_metadata SHOULD. Pre-baked OAuth continue_url removed entirely. * Security Considerations: new "Authentication challenges" bullet. Platforms MUST drive flow from structured scope/error params; error_description is hint-only and MUST NOT control flow. realm MUST match issuer URI for cross-protection-space correlation.
igrigorik
left a comment
There was a problem hiding this comment.
FYI, stacked a few more changes to document identity_optional and adopt WWW-Authenticate. Please sanity check, but otherwise I believe this is now good to land.
Document the architectural split between UCP and OAuth (RFC 8414) responsibilities that the rest of the spec relies on but never articulates. Anchors the four moving parts: * UCP config.scopes — hard gates (required auth) * OAuth scopes_supported — accepted scope vocabulary * Diff (scopes_supported ∖ config.scopes) — optional layer * UCP messages[] — runtime contextual hints (e.g., identity_optional)
…Authenticate Bearer changes: 1. Add no-token identity_required example The existing example only showed the token-present-but-expired case (error="invalid_token"). RFC 6750 §3.1 says error SHOULD be omitted when no token was presented — the more common case for a first request to a gated operation. Added a second labeled example for the no-token case so implementers aren't led to emit error="invalid_token" unconditionally. 2. Fix identity_optional section direction The section intro described identity_optional as "a mechanism for the platform to inform the buyer." The direction was reversed — the business emits this code in its response; the platform receives it and may present it to the user. Corrected to accurately describe the emitter and receiver. 3. Simplify identity_optional to remove misleading description coupling The section stated that per-scope description fields convey context for optional authentication, and that businesses SHOULD populate them when emitting identity_optional. This is incorrect: description is a field on scope_policy objects in config.scopes, which is the hard-gate (required) layer. Scopes relevant to identity_optional are by definition in the optional layer (scopes_supported ∖ config.scopes) and have no corresponding UCP schema field for descriptions. Removed the two-mechanisms paragraph and the SHOULD guidance on populating descriptions, as both described a mechanism that doesn't exist for optional-layer scopes. Upgraded identity_optional emission from MAY to SHOULD, since content on the message is the only available value prompt mechanism.
Overview
This PR establishes the OAuth 2.0 foundation for the Identity Linking capability
(
dev.ucp.common.identity_linking). It is an intentionally scoped first step —business-hosted OAuth 2.0 only — designed so that delegated identity providers
and non-OAuth auth mechanisms can be added in future PRs as non-breaking
extensions.
Context: #265 introduced a mechanism registry and capability-driven scopes
but shipped a critical bug where the intersection algorithm's scope-dependency
pruning rule gated checkout behind identity linking, breaking guest checkout.
#329 reverted it. #330 proposed a full redesign including delegated IdP and
identity chaining. This PR carries forward the improvements from both without
the delegated IdP complexity, and lays explicit extensibility groundwork for
that work to land cleanly.
What Changed
New:
source/schemas/common/identity_linking.jsonSchema for the identity linking capability. Two context-specific views, nested
under
"dev.ucp.common.identity_linking"in$defsper the project'scapability schema convention:
platform_schema: passthrough — platforms advertise support; noauth-specific config needed.
business_schema: requiresconfig.capabilities— a map declaringwhich capabilities offer buyer-scoped features and whether buyer identity
is required for each.
The
configobject usesadditionalProperties: truewith a$commentnaming one reserved extension point:
providers— map of trusted identity providers keyed by reverse-domain,with a
typediscriminator defaulting tooauth2. This single extensionpoint covers delegated IdP, identity chaining, and future non-OAuth
mechanisms such as wallet attestation (future PR).
New:
identity_requirederror code (error_code.json)Standard protocol signal for the case where a capability is configured with
auth_required: trueand a request arrives without a buyer identity token.Businesses MAY include a
continue_urlin the error body for buyeronboarding flows.
Rewrite:
docs/specification/identity-linking.mdMajor rewrite of the spec. Key sections:
Access levels. Capabilities operate at three access levels — public,
agent-authenticated, buyer-authenticated. Identity linking upgrades capabilities
to buyer-authenticated access. Capabilities are never excluded from
capability negotiation based on identity linking. This directly addresses the
root cause of the #265 bug.
Security hardening (carried forward from #265 and #330):
S256: MUST for all authorization code flows;plainMUST NOT be usedissvalidation: MUST (RFC 9207) — prevents Mix-Up Attacks; validation isunconditional, not gated on presence of the
issparameterredirect_urimatching: MUST — no partial/prefix matchingissuerbyte-for-byte match: MUST — no normalization (trailing slash strippingis a known
issvalidation bypass)scopes_supported: MUST in RFC 8414 metadata — enables early scope mismatchdetection before consent screen
Strict discovery hierarchy (carried forward from #265 and #330):
/.well-known/oauth-authorization-server) —2xx: use it;404:proceed to step 2; any other non-2xx response, network error, or timeout:
MUST abort, MUST NOT fall through to step 2
/.well-known/openid-configuration) —2xx: use it; anynon-2xx response, network error, or timeout: MUST abort
Capability-driven scope model (redesigned from #265, aligned with #330):
Scope declarations live in
config.capabilitieson the identity linking config,not as
identity_scopesannotations on individual capability schemas. This isarchitecturally correct because whether a capability requires buyer auth is a
business decision — a B2B wholesaler gates catalog access, a B2C retailer
doesn't. Per-schema annotations can't express this variance.
Scope naming uses capability names directly (
dev.ucp.shopping.checkout,dev.ucp.shopping.order:read) — reusing UCP's existing reverse-DNS governanceand eliminating a separate scope namespace. Replaces the old
ucp:scopes:checkout_sessionformat.Future Extensibility section — explicit normative spec for how future
versions extend this capability without breaking v1 implementations (see below).
Updated:
docs/specification/overview.mdIdentity linking added back to both business and platform profile examples
using the new config shape, including the
auth_requiredfield naming.Fixed:
docs/index.mdScope naming in the RFC 8414 metadata example updated from
ucp:scopes:checkout_sessiontodev.ucp.shopping.checkout.Forward Compatibility Design
The schema and spec are explicitly designed for non-breaking extension via a
single
config.providersextension point.config.providers— Delegated Identity Providers and Mechanism ExtensibilityA future PR will add a
config.providersmap (keyed by reverse-domainidentifier) allowing businesses to declare trusted external identity providers
alongside their own hosted OAuth server. Each provider entry carries a
typediscriminator defaulting to
oauth2, making the map extensible to non-OAuthmechanisms (wallet attestation, verifiable credentials) without introducing a
separate
mechanismsarray.This covers two use cases:
to new businesses without a browser redirect (per
draft-ietf-oauth-identity-chaining-08), solving the N-merchant = N-OAuth-dances
problem for agentic commerce.
oauth2typevalueenable wallet attestation and similar schemes. Platforms select the first
entry whose
typethey support — analogous to TLS cipher suite negotiation,preserving business-preference ordering.
This addition is non-breaking because:
configusesadditionalProperties: true— the schema will not reject thenew field
recognize
config.providersMUST ignore it and fall back to RFC 8414discovery on the business domain
Type of Change
ucp:scopes:*format)Breaking Changes Justification
The scope naming convention change (
ucp:scopes:checkout_session→dev.ucp.shopping.checkout) is breaking for any implementation that hardcodedthe old format. The old format was defined in the pre-#265 spec and was never
part of a stable release — the capability was at Working Draft status throughout.
The new format is consistent with UCP's reverse-DNS naming governance and
eliminates the need for a parallel scope namespace.
Checklist