feat!: redesign identity linking with mechanism registry and capability-driven scopes#265
Conversation
|
Great design. The mechanism registry pattern is exactly right — hardcoding OAuth as the only path was the main limitation of the previous spec. This pairs well with #264 (attestation extension for eligibility). The two address different surfaces:
But there's also a natural overlap: wallet attestation as an identity mechanism type. Some commerce flows don't need OAuth account linking at all — the wallet is the identity. A business selling token-gated merchandise needs to verify holdings, not link an account. The registry already supports this cleanly: "supported_mechanisms": [
{
"type": "oauth2",
"issuer": "https://auth.merchant.example.com"
},
{
"type": "wallet_attestation",
"provider_jwks": "https://verifier.example.com/.well-known/jwks.json",
"attestation_endpoint": "https://verifier.example.com/v1/attest"
}
]The platform calls the attestation endpoint, receives a signed payload with For scope derivation: a One question on the spec: the Discovery Bridging section (RFC 8414 / OIDC fallback hierarchy) is tightly coupled to OAuth. Would it make sense to scope that hierarchy under the |
cusell-google
left a comment
There was a problem hiding this comment.
This redesign is a massive improvement, left some comments. The only real blocker is related to the issuer normalization. Thanks Amit!
…ation for identity linking
…efine a metadata resolution hierarchy, and clarify scope derivation in capability negotiation.
…t schemas for OAuth2 scope derivation.
… discovery failure, and introduce scope dependency pruning.
…idation requirements, and remove `identity_scopes` from the capability schema.
…necessary properties from the JSON schema
…identity linking documentation with mechanism selection algorithm and scope naming conventions.
…runing rules and enhancing identity linking schema description with annotation conventions.
…on rules and enhance schema definition for authentication mechanisms.
…and update scope derivation language in documentation.
…zation details and enhance JSON schema by adding required config property.
…tion to ensure clear and grouped permission presentation for users.
0d3fc0a to
086f2cb
Compare
… by specifying exact scope requests and enforcing strict issuer comparison without normalization.
Hi Douglas, wallet_attestation would be a good idea to add in a future PR. I wanted to submit the redesign for OAuth for this PR, so that other schemes can be added in future. |
You're right — I re-read the updated spec and Discovery Bridging is correctly scoped as a subsection of |
drewolson-google
left a comment
There was a problem hiding this comment.
This generally seems good to me. The one concern I have is around negotiated scopes and linking failures. See my comment below.
|
|
||
| 1. **Schema Declaration:** Each individual capability schema explicitly defines | ||
| its own required identity scopes (e.g., `dev.ucp.shopping.checkout` declares | ||
| `dev.ucp.shopping.scopes.checkout_session`). |
There was a problem hiding this comment.
Help me understand this a bit. Capabilities will work with a linked identity and without a linked identity -- checkout is a good example of this. Is this idea that if an identity is linked then the link must contain all of the scopes from the negotiated set of capabilities?
If so, this feels a bit odd, in that the capabilities worked before identity linking, when there were no scopes at all, and could work post-identity linking even without the appropriate scopes by falling back to the behavior without a linked identity.
Thoughts on this?
There was a problem hiding this comment.
@drewolson-google
No, This is to facilitate identity linking itself not the case where identity is already linked.
Today, the discovery of identity linking url's and scopes is not very well defined via UCP. This PR allows us to have a UCP capability dev.ucp.common.identity_linking , where you can find out the auth scheme, config that both platform and business can support, algorithm for agents to facilitate identity_linking. The scopes are used during this initial identity_linking flow based on the capabilities.
Once the identities are linked, this configuration is not used as the tokens are used directly to facilitate authentication can call operations on capabilties. Let me know if that makes sense or if you suggest I add some more documentation to clarify this piece better somewhere.
There was a problem hiding this comment.
Got it, makes sense, thanks!
jingyli
left a comment
There was a problem hiding this comment.
Added some nits/naive questions but overall looks good!
Non-blocking, but one side question I'm curious about is how do we think about extension application (if any) on this new identity linking capability moving forward given the schema is focused on the discovery & negotiation of authorization/linking between platforms & businesses?
| returns exactly `404 Not Found`, the platform **MUST** append | ||
| `/.well-known/openid-configuration` to the defined `issuer` string and fetch. | ||
| If this final fetch returns any non-2xx response or a network error, the | ||
| platform **MUST** abort the identity linking process. |
There was a problem hiding this comment.
nit: This should also be abort the discovery process for consistency with the steps above..?
There was a problem hiding this comment.
This is the last stage of discovery so if this fails, we want platform to abort the identity linking process. We could have said "abort the discovery process" but I think both are valid. Let me know if you feel strongly about changing this. I don't have a strong opinion and both seem ok to me.
|
|
||
| #### For businesses | ||
|
|
||
| - **MUST** implement OAuth 2.0 |
There was a problem hiding this comment.
A super naive QQ: Here we are saying business MUST implement OAuth 2.0, a few lines above we also mention **MUST** implement the OAuth 2.0 Authorization Code flow for platforms.
Given these 2 bullets, wouldn't the negotiated mechanism type always be oauth2 (which feels like it's going against the main design point around the mechanism registry)..?
There was a problem hiding this comment.
These are both mentioned under OAuth 2.0 ("type": "oauth2") heading, so they apply only when Supported Mechanism is oauth2. When we add new mechanisms types, we will have a separate section under Supported Mechanisms heading for that mechanism, at the same level as OAuth 2.0. Does that make sense?
| the finalized intersection. If a capability (e.g., `order`) is excluded from | ||
| the active capability set, its respective scopes **MUST NOT** be requested by | ||
| the platform. If the final derived scope list is completely empty, the platform | ||
| **SHOULD** abort the identity linking process, as there are no secured resources |
There was a problem hiding this comment.
Curious why this guideline is a SHOULD rather than a MUST if there are no secured resources to authorize as part of the linking process (is there ever a use case for showing an empty consent screen)?
There was a problem hiding this comment.
Good catch.
The only weak argument for keeping SHOULD could be forward-compatibility with future mechanism types that don't use scopes — but the spec already handles that through the mechanism registry: future non-scope-based mechanisms would define their own requirements in their own section and wouldn't be constrained by this sentence.
I will flip it to MUST.
| - For single-parent extensions (`extends: "string"`): parent must be present | ||
| - For multi-parent extensions (`extends: ["a", "b"]`): at least one parent | ||
| must be present | ||
| - **Scope Dependencies**: Remove any capability declaring `identity_scopes` |
There was a problem hiding this comment.
Naive QQ on this: Given the UCP checkout capability will declare identity_scopes per the change on checkout.json schema below, does this mean it will always be removed if dev.ucp.common.identity_linking is not part of the intersection between business & platform?
There was a problem hiding this comment.
Yes, I think that is how it should work. identity_scopes on checkout.json is checkout's declaration that it requires an authorized Bearer token to operate. Without identity_linking in the intersection, there's no negotiated mechanism to obtain that token — so keeping checkout active would leave the platform with a capability it can't securely call. Pruning it is the correct outcome.
In practice this means identity_linking acts as a soft prerequisite for any capability declaring identity_scopes. Both parties need to advertise it for checkout to survive the intersection.
Good callout though, let me know if we should add something more to the documentation to make it clearer.
There was a problem hiding this comment.
Does this mean you can only use the checkout capability if you also use the identity linking capability?
…tion of the process when no secured resources are available and refine scope mapping for CheckoutSession.
Yes, that may not make sense for this capability, given the purpose is to define the mechanism/config for identity_linking. It will just extend via new mechanism types. If you were thinking about the loyalty use cases, I think the extensions there would probably apply to cart/checkout/order etc. For identity management, I think we will probably need a new capability (e.g Profile or Account) which will be a separate from this capability (identity_linking). Let me know if that makes sense. |
Overview
This PR redesigns the Identity Linking capability from a hardcoded OAuth 2.0
specification into an extensible, security-hardened system built on three
core principles: a Mechanism Registry pattern for future-proof
authentication negotiation, capability-driven least-privilege scope
derivation, and hardened OAuth 2.0 security requirements aligned with
current best practices.
What Changed
Mechanism Registry Pattern (
identity-linking.md,identity_linking.json)The capability no longer hardcodes OAuth 2.0 as the only authentication
mechanism. Instead, businesses declare an ordered
supported_mechanismsarray in their capability config, and platforms select the first mechanism
type they support (business-preference ordering, mirroring TLS cipher suite
negotiation).
A formal Mechanism Selection Algorithm is defined:
typethe platform supportsThis enables future mechanism types (e.g.,
verifiable_credential) withoutbreaking changes to the protocol.
Capability-Driven Least-Privilege Scope Negotiation (
overview.md,checkout.json,identity_linking.json)Authorization scopes are no longer hardcoded or pre-declared in the identity
linking config. Instead:
identity_scopesannotation (e.g.,checkout.jsondeclaresdev.ucp.shopping.scopes.checkout_session)capabilities excluded during negotiation contribute zero scopes
identity_scopesare removed ifdev.ucp.common.identity_linkingis notin the intersection
computed from the stabilized intersection only
This makes over-permissioning impossible by construction, not convention.
Scope Naming Convention
Scopes now use reverse DNS dot notation with a mandatory
.scopes.segment, consistent with UCP capability names:
dev.ucp.<domain>.scopes.<capability>(e.g.,
dev.ucp.shopping.scopes.checkout_session)<reverse-dns>.scopes.<capability>(e.g.,
com.example.loyalty.scopes.points_balance)A regex pattern enforcing this convention is defined in
$defs/identity_scopesinidentity_linking.jsonas the canonicalreference.
Hardened OAuth 2.0 Security (
identity-linking.md)The OAuth 2.0 mechanism requirements are significantly tightened:
S256issvalidation (RFC 9207)redirect_urimatchingDiscovery Resolution Hierarchy (
identity-linking.md)A formal 3-tier hierarchy replaces the previous unspecified discovery:
discovery_endpoint(hard abort on any failure)/.well-known/oauth-authorization-server(only404triggers fallback;
5xx, timeouts abort)/.well-known/openid-configuration(abort on non-2xx)Schema (
identity_linking.json)New schema file with:
platform_schema: passthrough only — platforms are consumers ofmechanisms, not providers
business_schema: requiresconfigwithsupported_mechanisms(
minItems: 1)mechanism: open base type (consistent withpayment_credential.json)—
typerequired,additionalProperties: trueoauth2: named$deffor explicit strict validationidentity_scopes: canonical annotation definition with enforced regexpattern
End-to-End Workflow Example (
identity-linking.md)A complete walkthrough added: AI Shopping Agent (platform) + Merchant
(business), covering discovery, scope derivation, mechanism selection,
OAuth authorization request (with PKCE and
issparameters), and tokenexchange.
Files Changed
docs/specification/identity-linking.mddocs/specification/overview.mdsource/schemas/common/identity_linking.jsonsource/schemas/shopping/checkout.jsonidentity_scopesannotationdocs/index.mdType of change
Please delete options that are not relevant.
Is this a Breaking Change or Removal?
If you checked "Breaking change" above, or if you are removing any schema
files or fields:
!to my PR title (e.g.,feat!: remove field).Checklist