Conversation
drewolson-google
left a comment
There was a problem hiding this comment.
Generally looks very good to me. I've included a few small comments.
|
A couple of key takeaways from today's working group call... 1. Do we really need request signatures?Yes and no.
Recommendation: "Businesses SHOULD authenticate agents". The spec should allow multiple mechanisms (API keys, signatures, MTLS) without mandating a single approach. Signatures become valuable when you want permissionless agent onboarding, and we should spec this path. 2. If agent auth is the primary goal, can we simplify what we're signing?Likely, yes. Current proposal signs the entire request body with JCS canonicalization, which is complex and might be unnecessary — see 3. We could, potentially, skip signing body or consider As a next step, we'll tap security folks to review and help converge on a recommendation. 3. Bonus: can we unify on RFC 9421?Likely, yes. UCP's MCP transport runs over HTTP, which means we can rely on 9421: single signature mechanism, CDN/edge can validate, no JCS needed. This would be a significant simplification. |
|
@ACSchil fyi, updated to capture shape we discussed yesterday, ptal. |
Context / Closes #135. Defines a layered signature architecture: SHARED FOUNDATION ├── Canonicalization: JCS (RFC 8785) ├── Algorithms: ES256 (required), ES384, ES512 ├── Key Format: JWK (RFC 7517) ├── Key Discovery: signing_keys[] in /.well-known/ucp └── Replay Protection: idempotency-key (business layer) │ ├── REST BINDING (RFC 9421) │ Headers: Signature, Signature-Input, UCP-Content-Digest-JCS │ └── MCP BINDING (RFC 7515 Appendix F) Fields: meta.signature, meta.idempotency-key, meta.ucp-agent JCS canonicalization (RFC 8785): - Deterministic JSON serialization before signing - Avoids whitespace/key-ordering verification failures - Custom header `UCP-Content-Digest-JCS` (not RFC 9530 Content-Digest) because we hash canonicalized JSON, not raw bytes Transport-specific formats: - REST uses RFC 9421 HTTP Message Signatures (modern standard) - MCP uses detached JWS (RFC 7515 Appendix F) in meta.signature - Both sign the full message; MCP excludes only meta.signature field Changes to OpenAPI: - `Request-Signature` header → `Signature` + `Signature-Input` headers - `X-Detached-JWT` response header → `Signature` + `Signature-Input` - `UCP-Content-Digest-JCS` header for body digest Other updates: - checkout-rest.md, checkout-mcp.md: Added Message Signing sections - order.md: Rewrote webhook signing to use RFC 9421 - ap2-mandates.md: Now references signatures.md for crypto primitives - openrpc.json: Added signature field to meta, added meta to cart methods - mkdocs.yml: Added signatures.md to nav and llmstxt sections
- Separate "Implementation requirements" (MUST verify ES256) from
"Usage guidance" (SHOULD use ES256 for compatibility)
- Removed "Signers: use newest key" - no way to determine key age
from JWK spec, and verifiers accept any key in signing_keys[]
This restructures UCP's signature specification based on working group discussion. Key changes: Multiple authentication mechanisms: - HTTP Message Signatures, API keys, OAuth 2.0, mTLS, choose whichever - Businesses choose what fits their security model and integration patterns - Implementations SHOULD maintain allowlists of trusted profiles RFC 9421 for all HTTP transports: - REST and MCP (streamable HTTP) now use the same signing mechanism - Signature + Signature-Input headers replace the deprecated Request-Signature - Covered components: @method, @path, content-digest, ucp-agent, idempotency-key Content-Digest (raw bytes) replaces JCS canonicalization: - Request signatures hash raw body bytes per RFC 9530 - Simpler implementation, no JSON canonicalization complexity - JCS retained only for AP2 mandates
93b7131 to
7e99404
Compare
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
- drop ES512/P-521 (not in IANA HTTP Signature Algorithms registry) - clarify algorithm derived from JWK `crv`, not Signature-Input `alg` - add ECDSA signature encoding requirement: raw r||s, not ASN.1/DER - add `@authority` to required signed components (prevents cross-host replay) - add `ucp-agent` to signed components table (binds identity to signature) - add identity binding requirement across all auth mechanisms - split webhook signing to MUST; keep other responses as RECOMMENDED - fix normative language: MAY choose auth mechanism, MUST sign fully once chosen - fix OpenAPI schema: signature headers required: false (conditional on auth mechanism)
RFC 9421 (HTTP Message Signatures) deliberately leaves key discovery to the application: "a means of retrieving the key material... is within the purview of the application and outside the scope of this specification." UCP fills this gap. UCP profiles serve dual purpose — they declare capabilities for negotiation and publish signing keys for identity verification. This means the same fetch that enables capability negotiation also resolves the signing keys needed for RFC 9421 signature verification. The new Identity & Authentication section in overview.md defines this protocol: Key Discovery: profile URL -> fetch -> match keyid to kid in signing_keys[] -> verify signature. Both directions use the same mechanism (businesses at .well-known/ucp, platforms via UCP-Agent header). Profile Hosting: HTTPS-only, no redirects, Cache-Control with public and max-age >= 60s. Profiles are stable identity documents, not per-request configuration. Profile Fetching: two-tier trust model — a registry of pre-approved platforms with cached identity, and dynamic profile discovery for unrecognized platforms bounded by a fixed discovery footprint (LRU cache, global rate limit, backoff, async 503+Retry-After). Also defines authentication mechanism compatibility (API keys, OAuth, mTLS, HTTP Message Signatures) and identity binding requirements (authenticated identity must match UCP-Agent header). Slims signatures.md to signing/verification mechanics only; removes Profile Trust Model section (replaced by normative requirements in overview.md). Fixes orphaned validate_profile_url() reference, aligns error terminology with registry concept, and scopes the .well-known/ucp path constraint to business profiles only.
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
|
@maximenajim ty sir, sharp eyes! I believe all the flags should be addressed, please take another pass. 🙇🏻 |
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
| 1. Profiles **MUST** be served over HTTPS. | ||
| 2. Profile endpoints **MUST NOT** use redirects (3xx). | ||
| 3. Profile responses **MUST** include a `Cache-Control` header with | ||
| `public` and `max-age` of at least 60 seconds. Profiles **MUST NOT** |
There was a problem hiding this comment.
How should we handle the profile discovery when we get no-store or no-cache directives ? Reject, default to min TTL ?
Add 6 missed post-release changes discovered during full audit of 58 PRs merged after the 2026-01-23 branch was cut: Universal-Commerce-Protocol#16 — Optional request/response signing headers (PR Universal-Commerce-Protocol#156) [Additive] Universal-Commerce-Protocol#17 — Eligibility claims & provisional discounts (PR Universal-Commerce-Protocol#250) [High/Breaking] Universal-Commerce-Protocol#18 — Optional signals field on cart and checkout (PR Universal-Commerce-Protocol#203) [Additive] Universal-Commerce-Protocol#19 — Fulfillment method id/type optional on update (PR Universal-Commerce-Protocol#143/Universal-Commerce-Protocol#196) [Low] Universal-Commerce-Protocol#20 — intent field added to context (PR Universal-Commerce-Protocol#95) [Additive] Universal-Commerce-Protocol#21 — available_instruments added to payment handler (PR Universal-Commerce-Protocol#187) [Additive] Also updates PR_URLS map and changes/visible counts from 15 → 21. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 6 missed post-release changes discovered during full audit of 58 PRs merged after the 2026-01-23 branch was cut: Universal-Commerce-Protocol#16 — Optional request/response signing headers (PR Universal-Commerce-Protocol#156) [Additive] Universal-Commerce-Protocol#17 — Eligibility claims & provisional discounts (PR Universal-Commerce-Protocol#250) [High/Breaking] Universal-Commerce-Protocol#18 — Optional signals field on cart and checkout (PR Universal-Commerce-Protocol#203) [Additive] Universal-Commerce-Protocol#19 — Fulfillment method id/type optional on update (PR Universal-Commerce-Protocol#143/Universal-Commerce-Protocol#196) [Low] Universal-Commerce-Protocol#20 — intent field added to context (PR Universal-Commerce-Protocol#95) [Additive] Universal-Commerce-Protocol#21 — available_instruments added to payment handler (PR Universal-Commerce-Protocol#187) [Additive] Also updates PR_URLS map and changes/visible counts from 15 → 21. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Context / Closes #135. Defines a layered signature architecture:
JCS canonicalization (RFC 8785):
UCP-Content-Digest-JCS(not RFC 9530 Content-Digest)because we hash canonicalized JSON, not raw bytes
Transport-specific formats:
Changes to OpenAPI:
Request-Signatureheader →Signature+Signature-InputheadersX-Detached-JWTresponse header →Signature+Signature-InputUCP-Content-Digest-JCSheader for body digestOther updates:
Checklist