Skip to content

docs(order): webhook example must sign ucp-agent (identity otherwise unbound) - #568

Closed
vishkaty wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
vishkaty:fix/webhook-example-sign-ucp-agent
Closed

docs(order): webhook example must sign ucp-agent (identity otherwise unbound)#568
vishkaty wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
vishkaty:fix/webhook-example-sign-ucp-agent

Conversation

@vishkaty

@vishkaty vishkaty commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

The "Example Webhook Request" in docs/specification/order.md carries a UCP-Agent header but its Signature-Input signs only ("@method" "@authority" "@path" "content-digest" "content-type") — omitting ucp-agent.

This contradicts the normative signed-component rules in signatures.md:

  • The request signed-components table marks ucp-agent "Required if UCP-Agent header is present" (the ** footnote), and there is no separate webhook component table.
  • signatures.md explicitly derives webhook identity from the UCP-Agent header ("Business → Platform webhooks: Profile URL from UCP-Agent header") and requires webhooks to be signed.

Because the example doesn't sign ucp-agent, the sender's identity (the component's documented purpose — "binds identity") is not cryptographically bound in the illustrated signature, and a platform copying the example produces a signature that a table-following verifier would reject.

One-line fix: add ucp-agent to the example's signed components, matching the request example in signatures.md.

Found while building an unofficial UCP conformance test suite (https://spck.dev); verified against main @ 7e5fc42.


Update (class sweep): re-checked every RFC 9421 example in docs/ for this same pattern (UCP-Agent header present but not among the signed components). One more instance: checkout-rest.md's "Example Signed Request" — fixed in the second commit with the same one-line change, matching the component order of the canonical request example in signatures.md. All other examples either already sign ucp-agent, omit the header entirely, or are response/elided examples where the component doesn't apply.

@vishkaty
vishkaty requested review from a team as code owners July 7, 2026 05:08
@damaz91 damaz91 added the documentation Improvements or additions to documentation label Jul 14, 2026
@damaz91 damaz91 added status:needs-triage Signal that the PR is ready for human triage and removed status:needs-triage Signal that the PR is ready for human triage labels Jul 14, 2026
vishkaty added a commit to vishkaty/ucp that referenced this pull request Jul 16, 2026
…alues

Extends the sweep per review: the response-signing examples used a second
DER shape (MFQCIH..., a 0x30-led SEQUENCE) that the first pass's pattern
missed — now swept by decoding every example signature value's leading byte
rather than pattern-matching. Fixes checkout-mcp.md, checkout-rest.md, and
signatures.md's response-signing example, and aligns the stale
keyid="merchant-2025" with the merchant-2026 used by every sibling example.
order.md's instance remains deferred to avoid overlapping Universal-Commerce-Protocol#568.
@damaz91
damaz91 requested review from DanielFalconGuedes and jingyli and removed request for a team July 20, 2026 13:28
damaz91 pushed a commit that referenced this pull request Jul 24, 2026
…requires (#590)

* docs(signatures): make example signature values raw r||s as the spec requires

signatures.md mandates fixed-width raw r||s encoding for ECDSA signature
values ("MUST use fixed-width raw r||s encoding"), but the worked examples
showed values beginning ME[UQ]C... — the base64 of a DER SEQUENCE, the exact
encoding the normative text forbids. An implementer copying the examples'
shape produces signatures a conformant verifier rejects.

Replaces the seven example values in signatures.md, checkout-mcp.md,
checkout-rest.md, and order-rest.md with a genuine ES256 raw r||s value
(64 bytes, 88 base64 chars), preserving each instance's truncation style.
The instance in order.md is deliberately left to avoid overlapping #568,
which edits the same example block.

Fixes #569.

* docs(signatures): fix remaining DER-shaped response-signing example values

Extends the sweep per review: the response-signing examples used a second
DER shape (MFQCIH..., a 0x30-led SEQUENCE) that the first pass's pattern
missed — now swept by decoding every example signature value's leading byte
rather than pattern-matching. Fixes checkout-mcp.md, checkout-rest.md, and
signatures.md's response-signing example, and aligns the stale
keyid="merchant-2025" with the merchant-2026 used by every sibling example.
order.md's instance remains deferred to avoid overlapping #568.
katyalai added 2 commits July 24, 2026 09:34
The example webhook request carries a UCP-Agent header but its
Signature-Input omits the ucp-agent component. signatures.md's request
signed-component table requires ucp-agent whenever the UCP-Agent header
is present, and webhook identity is derived from that header — so the
example as written leaves the sender identity cryptographically unbound
and contradicts the table it illustrates.
Same class as the order.md webhook example fix: the example carries the
UCP-Agent header but omits ucp-agent from the covered components, leaving
the signer identity unbound. checkout-rest.md itself requires the header
on all requests, so per the signatures.md component table it must be
signed. Matches the component order of the canonical request example in
signatures.md.
@vishkaty
vishkaty force-pushed the fix/webhook-example-sign-ucp-agent branch from 0ece9a2 to 4b35d52 Compare July 24, 2026 13:37
vishkaty pushed a commit to vishkaty/ucp that referenced this pull request Jul 31, 2026
… webhook signature

The order event webhook lists Webhook-Id and Webhook-Timestamp as required
headers and states that headers follow Standard Webhooks, but the signing and
verification steps never cover them, and the example Signature-Input signs only
method, authority, path, content-digest, and content-type. Webhooks carry no
Idempotency-Key, so these headers are the only per-delivery replay signal; as
written a captured, validly signed delivery can be replayed with an altered
Webhook-Id or Webhook-Timestamp.

The overview verification gate already anticipates this: a header outside the
default covered set is gate-required when its defining section says so. Have the
order webhook section say so for webhook-id and webhook-timestamp, require
platforms to verify their coverage, and require deduplication on Webhook-Id.
Update the signing and verification steps and the example accordingly.

The example also now covers ucp-agent, which the overview gate already requires
whenever the UCP-Agent header is present (it was missing from this example); this
binds the signer identity on the webhook and subsumes the webhook-example change
in Universal-Commerce-Protocol#568. Also drop the stale event_id and created_time from the example body and
the playground payload builder (moved to headers in Universal-Commerce-Protocol#342), and show the required
Webhook-Id and Webhook-Timestamp headers in the example.
@vishkaty

Copy link
Copy Markdown
Contributor Author

Superseding this with #659.

This PR's purpose is signing ucp-agent in the order webhook example. #659 now covers that example more completely: it adds ucp-agent (binding signer identity, the point of this PR) alongside webhook-id and webhook-timestamp, and it adds the normative rule requiring those to be covered plus deduplication on Webhook-Id. Rather than leave two PRs editing the same example line, I am closing this in favor of #659.

This PR also added ucp-agent to a signed request example in checkout-rest.md, which #659 does not touch. That is a small, separate improvement; I can refile it on current main if it is wanted. Thank you.

@vishkaty vishkaty closed this Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation status:under-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants