Identity Linking Capability¶
- Capability Name:
dev.ucp.common.identity_linking - Schema:
https://ucp.dev/2026-08-25/schemas/common/identity_linking.json
Overview¶
The Identity Linking capability enables a platform to obtain authorization to perform actions on behalf of a user on a business's (relying party) site.
This linkage is foundational for user-authenticated commerce experiences: accessing loyalty benefits, personalized offers, saved addresses, wishlists, and order history. Capabilities without identity linking still operate at public or agent-authenticated access levels — identity linking upgrades the experience, it does not gate it.
This specification uses
OAuth 2.0
for authorization. Direct OAuth 2.0 against the business domain (via
Discovery) is always available. When the business declares
trusted external identity providers in config.providers, platforms
MAY instead chain identity from a provider via the
Accelerated IdP Flow, skipping the
browser-based flow when they already hold a suitable upstream token.
Participants¶
| UCP Role | Identity Role | Description |
|---|---|---|
| Platform | User Agent | Trusted intermediary that initiates identity linking and presents user identity tokens to businesses on behalf of the user. |
| Business | Authorization Server / Relying Party | Hosts its own OAuth 2.0 authorization server. Authenticates users and issues access tokens scoped to UCP capabilities. |
| User | Resource Owner | The person whose identity is being linked. Grants explicit consent to the platform during the OAuth authorization flow. |
Access Levels¶
Capabilities operate at three access levels:
| Level | Authentication | Example |
|---|---|---|
| Public | None | Browse a public catalog |
| Agent-authenticated | Platform credentials (client_id / client_secret) |
Guest checkout, create a cart |
| User-authenticated | Platform credentials + user identity token | Saved addresses, full order history, personalized pricing |
Identity linking bridges agent-authenticated to user-authenticated access: the platform obtains a user identity token by completing the OAuth flow described below, and presents it on subsequent requests.
Identity linking and capability negotiation are independent layers. A
capability is advertised and negotiated based on its own profile presence —
never excluded because identity linking is absent. Identity linking, when
present, declares the scopes that gate user-authenticated operations
within negotiated capabilities (see Scopes). A merchant whose
profile lists dev.ucp.shopping.order has it in the negotiated intersection
either way. If their profile also lists identity linking with
dev.ucp.shopping.order:read in config.scopes, operations covered by
that scope require a user identity token.
Business-Populated Response Values¶
When a request is user-authenticated, a business MAY populate response fields it owns for that user from its own stored state, rather than only echoing values the platform supplied.
Request annotations determine whether the platform may send a field; they do
not assign semantic ownership of its value. A field annotated
ucp_request: "omit" is excluded from requests and, when permitted and present
on a response, is populated by the business (for example, totals, messages,
and order). Fields annotated optional for an operation, or left unannotated,
may also be supplied by the platform in applicable requests. Where the response
schema and the field's semantic contract permit business-owned user state, the
business MAY populate it from stored state on the response (for example,
buyer and payment.instruments[]).
Identity linking provides the authenticated user context for these values.
Subject to the scopes that gate the operation, a business MAY return the
user's loyalty membership in loyalty (see Loyalty), saved
payment instruments in payment.instruments[] (see Checkout), or
buyer profile data in buyer.
Businesses MUST NOT return stored user-specific state unless the request is user-authenticated and authorized for the operation. They MUST only return values that belong to the authenticated user and are appropriate for the current transaction, and SHOULD limit them to the data needed for that transaction. Platforms MUST treat returned business-owned identifiers as opaque and business-scoped, and MUST NOT infer raw credentials from a display value.
UCP and OAuth¶
UCP defines commerce semantics (which scopes mean what, which gate which operations); OAuth (RFC 8414) defines identity machinery (endpoints, flows, accepted scope vocabulary); runtime messages carry per-request advisories.
- UCP
config.scopesdeclares hard gates: scopes that require user authentication for the operations they cover. - OAuth
scopes_supported(RFC 8414) declares the accepted scope vocabulary: every scope the authorization server will honor if requested. - The diff (
scopes_supported∖config.scopes) is the optional layer: scopes the merchant accepts but doesn't gate, used to advertise authentication-unlocked features without requiring auth. - UCP
messages[]carry runtime contextual hints: per-request notices likeidentity_optional(see Optional Authentication) signaling that authenticating would unlock value in the current context.
General Guidelines¶
For Platforms¶
-
MUST authenticate token endpoint requests using a method advertised in the business's
token_endpoint_auth_methods_supportedmetadata (RFC 8414):- Confidential clients (server-side platforms that can protect a
credential) SHOULD prefer asymmetric methods —
private_key_jwt(RFC 7523 §2.2) ortls_client_auth(RFC 8705) — and MAY useclient_secret_basic(RFC 6749 §2.3.1, RFC 7617) where the business supports it. - Public clients (native, desktop, browser-extension, and on-device
agent runtimes per
RFC 8252 §8.5)
MUST use
noneand rely on PKCE withS256(RFC 7636) as proof-of-possession of the authorization code. Public clients MUST NOT embed aclient_secret.
Platforms MUST select the strongest method offered by the business that is compatible with the platform's deployment model.
- Confidential clients (server-side platforms that can protect a
credential) SHOULD prefer asymmetric methods —
-
MUST include user identity tokens in the HTTP
Authorizationheader using the Bearer scheme:Authorization: Bearer <access_token>(RFC 6750 §2.1). - MUST process
WWW-Authenticate: Bearerchallenges per RFC 6750 §3 on401and403responses to user-authenticated operations. Platforms MUST extract thescopeparameter (when present) to construct subsequent authorization requests, and SHOULD follow theresource_metadatapointer (RFC 9728) when present to discover the protecting authorization server. - MUST implement the OAuth 2.0 Authorization Code flow (RFC 6749 §4.1) as the account linking mechanism.
- MUST use PKCE
(RFC 7636)
with
code_challenge_method=S256for all authorization code exchanges. - MUST validate the
issparameter in the authorization response (RFC 9207) to prevent Mix-Up Attacks. The platform MUST verify that theissvalue matches the authorization server's issuer URI (as declared in its RFC 8414 metadata). If the values do not match, the platform MUST abort and discard the authorization response. - SHOULD include a unique, unguessable
stateparameter in the authorization request to prevent CSRF (RFC 6749 §10.12). - When
config.providersis present, the platform MAY chain identity from a listed provider via the Accelerated IdP Flow. If no listed provider is supported or suitable, the platform MUST fall back to direct OAuth on the business domain via Discovery (see Identity Providers). - Before initiating identity chaining with a business, the platform SHOULD offer the user a choice of available identity providers and indicate which provider's identity will be shared with the business.
- Revocation and security events:
- MUST call the business's token revocation endpoint (RFC 7009) when a user initiates an unlink action on the platform side.
- SHOULD support OpenID RISC Profile 1.0 to handle asynchronous account updates and cross-account protection events initiated by the business.
For Businesses¶
- MUST implement OAuth 2.0 (RFC 6749).
- MUST publish authorization server metadata via
RFC 8414
at
/.well-known/oauth-authorization-server. - MUST populate
scopes_supportedin RFC 8414 metadata to allow platforms to detect scope mismatches before initiating an authorization flow. - MUST return the
issparameter in the authorization response (RFC 9207). - MUST enforce PKCE
(RFC 7636)
validation at the token endpoint for all authorization code exchanges.
Requests without a valid
code_verifierMUST be rejected. - MUST enforce exact string matching for the
redirect_uriparameter during authorization requests to prevent open redirects and token theft. Theredirect_uriin the token request MUST be identical to the one in the authorization request. Exception — loopback redirects: For redirect URIs targeting127.0.0.1or[::1], businesses MUST ignore the port component and match on scheme, host, and path only, to accommodate native and desktop clients that obtain an ephemeral port from the OS at runtime (RFC 8252 §7.3). - MUST declare supported client authentication methods in
token_endpoint_auth_methods_supported(RFC 8414) and enforce one of the declared methods at the token endpoint. Businesses SHOULD support at least one asymmetric confidential-client method (private_key_jwtortls_client_auth) and MAY supportnonefor public clients per RFC 8252. Whennoneis advertised, businesses MUST require PKCE withS256and MUST reject any authorization code redemption that lacks a validcode_verifier. Requests that fail the negotiated authentication method MUST be rejected withinvalid_client; requests that fail PKCE MUST be rejected withinvalid_grant. - MUST validate user identity tokens on every user-authenticated request:
verify
iss,aud(the business's resource server identifier),exp, scopes, andclient_id/azp(or equivalent) to confirm the token was issued to the authenticated platform client (RFC 9068 §4). - MUST emit a
WWW-Authenticate: Bearerchallenge per RFC 6750 §3 on401 Unauthorized(identity_required) and403 Forbidden(insufficient_scope) responses to user-authenticated operations. See Error Handling for the full normative requirements. - MUST implement token revocation
(RFC 7009).
Revoking a
refresh_tokenMUST also immediately invalidate allaccess_tokens issued from it. - MUST support revocation requests authenticated with the same client credentials used at the token endpoint.
- MAY declare trusted external identity providers in
config.providers(see Identity Providers). Businesses MUST only list providers they explicitly trust and MUST NOT list their own authorization server. - When the business lists external identity providers of
type: oauth2inconfig.providers, the business MUST support the JWT bearer assertion grant type (RFC 7523) at its token endpoint to accept JWT authorization grants from those IdPs, and MUST includeurn:ietf:params:oauth:grant-type:jwt-beareringrant_types_supportedin its RFC 8414 metadata. - SHOULD provide an account creation flow if the user does not already have
an account, or return a
continue_urlin anidentity_requirederror response (see Error Handling) pointing to an onboarding flow. - MUST support standard UCP scopes as defined in the Scopes section.
- SHOULD publish protected resource metadata at
/.well-known/oauth-protected-resource(RFC 9728) and reference it via theresource_metadataparameter inWWW-Authenticatechallenges. This lets platforms discover the authorization server protecting the resource without relying on domain conventions and prepares the deployment for future delegated domain conventions. The business MUST publish this metadata when the authorization server does not live on the business domain. - SHOULD support OpenID RISC Profile 1.0 to signal revocation and account state changes to platforms.
Discovery¶
UCP discovery is a three-step pipeline.
Step 1 — Resolve the AS issuer. Platforms fetch the business's
protected-resource metadata per
RFC 9728
and use the selected entry from authorization_servers as the AS
issuer. The AS issuer MAY be hosted on a different origin than the
business domain. If the business publishes no protected-resource
metadata, the AS issuer defaults to the business domain (single-host
deployments).
Step 2 — Fetch AS metadata. Using the issuer from Step 1, platforms resolve authorization-server metadata via a strict two-tier hierarchy. Well-known URLs are constructed per RFC 8414 §3.1 (the well-known segment is inserted between the host and any issuer path, not appended).
-
RFC 8414 (Primary): Fetch
https://{host}/.well-known/oauth-authorization-server{path}.2xxresponse: use this metadata. Discovery complete.404 Not Found: proceed to step 2.- Any other non-2xx response, network error, or timeout: MUST abort. MUST NOT proceed to step 2.
-
OIDC Discovery (Fallback): Fetch
{issuer}/.well-known/openid-configuration.2xxresponse: use this metadata. Discovery complete.- Any non-2xx response, network error, or timeout: MUST abort.
Platforms MUST NOT silently fall through on any error other than
404 in step 1.
Step 3 — Validate the issuer. The issuer value in the discovered
metadata MUST byte-for-byte match the AS issuer selected in Step 1
(per
RFC 8414 §3.3).
Platforms MUST NOT normalize (e.g., strip trailing slashes) before
comparison.
Account Linking Flow¶
Identity linking uses the OAuth 2.0 Authorization Code flow with PKCE.
Platform Business AS
| |
|-- (1) Discover metadata via RFC 8414 -->|
|<-- authorization_endpoint, token_endpoint, scopes_supported --|
| |
|-- (2) Authorization Request --------->|
| response_type=code |
| client_id, redirect_uri |
| scope=<derived scope set> |
| code_challenge (S256) |
| state |
| |
| [user authenticates and |
| grants consent at business] |
| |
|<-- (3) Authorization Response --------|
| code, state, iss |
| |
| Validate: state matches, iss matches |
| discovered issuer URI |
| |
|-- (4) Token Request ----------------->|
| grant_type=authorization_code |
| code, redirect_uri |
| code_verifier |
| client auth (per advertised |
| token_endpoint_auth_method) |
| |
|<-- (5) Token Response ----------------|
| access_token, refresh_token |
| token_type=Bearer, scope |
Step 2 — Scope set: Platforms derive the authorization scope set from the
business's config.scopes map (see Scope Derivation).
Platforms MUST request only the derived scope set — not a superset.
Step 3 — Validation: The platform MUST verify that the state
parameter matches the value sent in step 2, and that the iss parameter
matches the authorization server's issuer URI from discovered metadata.
If either check fails, the platform MUST discard the authorization
response.
Step 4 — PKCE: The code_verifier MUST correspond to the
code_challenge sent in step 2. Businesses MUST reject token requests
where code_verifier is absent or does not verify against the stored
code_challenge.
Identity Providers¶
The config.providers map declares external trusted identity providers
from which the business will accept chained identity via JWT bearer
assertions for the Accelerated IdP Flow. Each
key identifies an IdP namespace and maps to an array of mechanism
entries — an IdP MAY offer multiple token acquisition mechanisms
under a single key. The map is additive metadata on top of the
always-available direct OAuth path against the business domain (see
Discovery); for the chaining path, it is a closed
allowlist — a business MUST reject a JWT authorization grant
whose iss does not match a listed oauth2 mechanism entry (see
Business Token Issuance).
- When absent or empty: platforms run direct OAuth against the business domain via Discovery.
- When present: platforms MAY select a mechanism entry whose
typethey support and chain identity via the Accelerated IdP Flow — typically one belonging to an IdP they already hold a valid upstream token for. If no listed mechanism is supported or suitable, platforms MUST fall back to direct OAuth on the business domain. - Self-listing forbidden. Businesses MUST NOT list their own
authorization server in
config.providers. Chaining-to-self is degenerate (the same server would issue and validate the assertion), and direct OAuth is already available via Discovery. Platforms MUST ignore anyoauth2mechanism entry whoseauth_urlmatches the business's own issuer URI.
Provider Configuration¶
Each key in config.providers is a reverse-domain identifier for an IdP
namespace; its value is an array of mechanism entries.
A provider key is a reverse-domain identifier, not a schema-bearing entity:
it declares no schema URL, so the
Authority Binding (which binds an entity's
schema URL to its namespace authority) does not apply here. A provider's trust
anchor is its auth_url, governed by the discovery rules below; binding
auth_url to the provider's namespace authority is a possible future hardening,
tracked separately.
Each entry is described by its type:
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Provider mechanism discriminator. |