Skip to content

feat: Web Bot Auth (WBA) interop #483

Open
igrigorik wants to merge 26 commits into
mainfrom
feat/wba-interop
Open

feat: Web Bot Auth (WBA) interop #483
igrigorik wants to merge 26 commits into
mainfrom
feat/wba-interop

Conversation

@igrigorik

@igrigorik igrigorik commented May 27, 2026

Copy link
Copy Markdown
Contributor

UCP specifies RFC 9421 message signatures, but had incompatible restrictions and missing guidance for interoperating with Web Bot Auth (WBA):

  • Algorithm (hard block): the JWK schema accepted only EC keys; Ed25519 was not a valid UCP key type, so a UCP signer literally could not produce a WBA-conformant signature.
  • Key publication (undefined role): UCP's canonical field is signing_keys[]; WBA verifiers read an RFC 7517 keys[] JWK Set. UCP neither forbade keys[] nor defined its role.
  • Header semantics (undefined): UCP-Agent points at the UCP profile; Signature-Agent advertises WBA keys. UCP didn't define how a verifier picks between them.

This PR resolves all three with a single-signature, dual-audience shape: one key, one signing operation, one signature on the wire that both a UCP verifier (via UCP-Agent) and a WBA verifier (via Signature-Agent) accept. WBA's protocol draft §4.2.4 permits additional covered components, which makes this valid under both regimes once the algorithm is supported and header roles are defined.

What this PR does

  1. Open algorithm vocabulary. The JWK schema validates EC and OKP via if/then (open kty/crv/alg; unknown keys are tolerated, never whole-profile-rejected). ES256 stays the universal MUST baseline; EdDSA (Ed25519) is OPTIONAL for general verifiers and MUST for verifiers that support WBA.

  2. Profile as a JWK Set. The profile gains an optional top-level keys[] (RFC 7517) mirroring canonical signing_keys[]; when both are present they MUST list the same kids with semantically equivalent JWKs (identical RFC 7638 thumbprints). RFC 7517 §5 permits the extra members, so the document is simultaneously a UCP profile and a valid JWK Set — a signer MAY reuse it as its WBA key source. (keys[] is published as an interim step toward promoting it to canonical and deprecating signing_keys[] in a future UCP version — separate PR.)

  3. "Dual-audience" defined, with scoped requirements. A dual-audience signature carries tag="web-bot-auth", signs the signature-agent component, sets keyid to the key's RFC 7638 thumbprint, and includes created/expires (nonce SHOULD). For such keys the published kid MUST equal the thumbprint, so the WBA lookup (by thumbprint) and the UCP lookup (keyidkid) resolve the same key. WBA interop is request-scoped; responses use standard UCP signatures.

  4. Discovery: three equal Signature-Agent variants. type=jwks_uri (a JWK Set URL — point it at the profile), type=cimd (a Client ID Metadata Document whose jwks_uri may point back at the profile), type=directory (an origin hosting a well-known directory). UCP does not rank them — the right choice depends on the counterparty's verifier. The type parameter and its values are defined in draft-meunier-webbotauth-httpsig-directory-00 §4.1 (the renamed/re-adopted directory draft, formerly draft-meunier-http-message-signatures-directory). The directory's format, per-key self-signatures, and media type are deferred to the same draft §5.2 — UCP does not restate them.

  5. Verifier (Identity Resolution Algorithm): capability-based, not tag-gated. The tag is a hint, not a gate. A UCP verifier always resolves via UCP-Agent (so a UCP-only verifier never implements WBA discovery — it only needs Ed25519 + RFC 9421 §2.1.2 to verify the bytes); a WBA-aware verifier MAY additionally resolve via Signature-Agent. A closed, explicit covered-component gate is enforced in every regime, so a WBA-minimal signature can never authenticate a UCP request whose body/method/path/headers are unbound. keyid==thumbprint is MUST for WBA-shape signatures. Identity is the URL that supplied the verifying key; a Signature-Agent key is treated as the same identity as UCP-Agent only when the URLs match.

  6. Fetch safety. UCP specifies it for every identity-resolution dereference (profile, jwks_uri, CIMD), aligned with the WBA protocol draft's SSRF guidance: reject special-use IPs (RFC 6890) with a loopback carve-out for local dev, resist DNS rebinding, don't follow in-document URLs to internal addresses, and bound response size (a deployment guard — the schema sets no size limit).

On the wire

Default UCP signature (unchanged)

POST /checkout-sessions HTTP/1.1
Host: merchant.example.com
UCP-Agent: profile="https://platform.example/.well-known/ucp"
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Digest: sha-256=:X48E9q...:
Signature-Input: sig1=("@method" "@authority" "@path" "ucp-agent" "idempotency-key" "content-digest" "content-type");keyid="merchant-2026"
Signature: sig1=:base64_ecdsa_signature_value:

Dual-audience opt-in signature (new)

POST /checkout-sessions HTTP/1.1
Host: merchant.example.com
UCP-Agent: profile="https://platform.example/.well-known/ucp"
Signature-Agent: sig1="https://platform.example/.well-known/ucp";type=jwks_uri
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Digest: sha-256=:X48E9q...:
Signature-Input: sig1=("@method" "@authority" "@path" "signature-agent";key="sig1" "ucp-agent" "idempotency-key" "content-digest" "content-type");keyid="poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U";created=1738617600;expires=1738621200;tag="web-bot-auth"
Signature: sig1=:base64_ed25519_signature_value:

One signature on the wire. UCP verifiers route via UCP-Agent; WBA verifiers route via Signature-Agent. Both verify the same bytes against the same key, and both headers can point at the same /.well-known/ucp URL (or Signature-Agent can point elsewhere).

Backwards compatibility

  • Default UCP signers/verifiers are unchanged; ES256 stays the baseline.
  • Profiles publishing only signing_keys[] remain valid; keys[] is OPTIONAL.
  • WBA opt-in is per-signature; non-opting sites emit only UCP-Agent.

Future direction

A UCP+AP2+WBA merchant operates two keys today (Ed25519 for HTTP/WBA, ECDSA P-256 for AP2 merchant_authorization, per AP2 v0.2). Two upstream moves collapse this to one: (1) AP2 #268 relaxing the non-deterministic-signature requirement, and (2) UCP promoting keys[] to canonical. The wire format and verifier algorithm here already point at that future.

Routed to a follow-up tracking issue (not expanded here): promoting keys[]/jwks_uri/CIMD convergence + migration, and tag namespace reservation.

Checklist

  • Core Protocol
  • Contributing Guide followed
  • Documentation updated
  • Linting/formatting pass

igrigorik added 3 commits May 27, 2026 07:35
Extend the UCP signing-key JWK schema and signature spec to recognize
EdDSA (Ed25519) keys per RFC 8037, alongside the existing ECDSA
P-256/P-384 support.

ECDSA P-256 (ES256) remains the universal UCP baseline. EdDSA is
OPTIONAL for general UCP verifiers, with one specific implication:
verifiers that opt into Web Bot Auth (WBA) interop MUST support EdDSA
because WBA mandates Ed25519. Adding EdDSA as a recognized UCP
algorithm is the necessary substrate for that interop path.

Why
---

UCP today supports ECDSA only. Web Bot Auth standardizes on Ed25519
for HTTP transport identity. UCP signers wanting interop with WBA
verifiers cannot do so under an ECDSA-only UCP. This commit makes
Ed25519 a recognized UCP algorithm so it can be used; the WBA opt-in
story that uses it lands in subsequent commits.

This change is purely additive:

* ES256 verification remains a MUST (unchanged universal baseline).
* ES384 remains OPTIONAL (unchanged).
* EdDSA (Ed25519) is added as OPTIONAL for general verifiers; signers
  MAY use either algorithm and MAY publish keys of either type.
* The profile schema's jwk_public_key definition is split into EC and
  OKP shapes via oneOf, matching standard JWK conventions.

Verifier expectations
---------------------

* A general UCP verifier MAY implement ES256-only. Existing
  deployments need not upgrade for this commit's purposes.
* A verifier that wants to verify Web Bot Auth-compatible signatures
  MUST support EdDSA — that's a consequence of opting into WBA, not
  a UCP-imposed cost.

Signer guidance
---------------

* For maximum interoperability with UCP verifiers today: use ES256.
* For WBA interop: use Ed25519 and coordinate counterparty support,
  or publish both algorithms and select per request.

A merchant participating in both UCP HTTP transport (with WBA interop)
and AP2 mandate signing today operates two keys: Ed25519 for HTTP
transport, ECDSA P-256 for the AP2 merchant_authorization JWS (which
AP2 v0.2 currently requires to be non-deterministic). Both keys live
in the same signing_keys[] array and are selected by kid. If AP2
relaxes its algorithm rule in a future revision (tracked in AP2 #268,
google-agentic-commerce/AP2#268), the
merchant MAY collapse to a single Ed25519 key serving both layers;
the wire format does not change.

UCP-only sites that do not interact with WBA or AP2 may operate a
single ES256 key (the universal baseline) and ignore EdDSA entirely.

Schema
------

The jwk_public_key definition uses oneOf to enforce kty/crv
consistency:

* kty="EC" requires crv in {P-256, P-384} plus x and y coordinates.
* kty="OKP" requires crv="Ed25519" plus x (single public key value).

Private key material (d, p, q, dp, dq, qi, oth, k) remains explicitly
forbidden via the existing "not" guard.
A UCP integrator MAY opt their primary signature into a Web Bot Auth-
compatible shape, so the same RFC 9421 signature verifies under both
UCP and WBA verifiers with one key and one signing operation.
Existing UCP-only signers and verifiers change nothing.

What's added
------------

signatures.md gains a new §WBA Interop subsection describing the
opt-in:

- Algorithm SHOULD be Ed25519 (WBA convention).
- Signer MUST emit a Signature-Agent header alongside UCP-Agent. An
  RFC 8941 Dictionary Structured Field whose member's sf-string is
  an HTTPS URL pointing to a key directory; the member key matches
  the Signature-Input signature label.
- Signer MUST sign the signature-agent component with
  ;key="<label>" matching the Signature-Agent member key (RFC 9421
  §2.1.2 Dictionary-member component selection).
- keyid MUST be the JWK SHA-256 Thumbprint (RFC 7638; RFC 8037 §2
  for OKP).
- created, expires, tag="web-bot-auth" MUST be in @signature-params.
- The data: URI inline JWKS form is out of scope; Signature-Agent
  MUST carry an HTTPS URL.

UCP's existing signed components (ucp-agent, idempotency-key,
content-digest, content-type) stay in the signed list — WBA accepts
them as "additional components" per the architecture draft §4.2.3.

§REST Binding adds:
- Signature-Agent header row to the headers table (conditional on
  WBA opt-in).
- signature-agent;key= row to the REST Request Signing components
  table.
- Signature-Agent parsing rules subsection in §REST Request
  Verification, with the dictionary-member-by-label-match rule.

A complete WBA-shape request example shows one Ed25519 signature
covering both regimes' required bytes.

overview.md §Identity & Authentication picks up a short note about
the Signature-Agent header pointing to the signatures.md opt-in.

Why
---

UCP integrators wanting interop with WBA-conformant verifiers
previously needed two keys, two signatures per request, or to skip
interop entirely. WBA's architecture draft §4.2.3 explicitly permits
additional components beyond the WBA-required set, which makes a
single-signature dual-audience shape valid under both regimes.

Backwards compatibility
-----------------------

Fully additive. UCP-only signers and verifiers continue to operate
unchanged. WBA opt-in is per-signature; UCP verifiers built only for
UCP-default signatures may need to add RFC 9421 §2.1.2 Dictionary-
member component selection support to verify WBA-shape signatures
(this is part of being RFC 9421-conformant, not WBA-specific).
Add a top-level keys[] field to the UCP profile schema (RFC 7517
JWK Set), making a UCP profile a valid JSON Web Key Set. This
enables one document to serve as both a UCP profile and a Web Bot
Auth-compatible key directory at the same URL. Document deployment
patterns and the verifier's identity-resolution algorithm.

What's added
------------

source/schemas/profile.json:

- New top-level keys[] property, sibling to signing_keys[] and ucp.
- signing_keys[] stays canonical (every UCP verifier reads it).
  Description updated to point profiles toward keys[] as the optional
  JWKS-superset mirror for WBA-directory compatibility.
- When both arrays are published, they MUST list the same set of
  kid values, and entries sharing a kid MUST be semantically
  equivalent (identical RFC 7638 thumbprints; serialization
  differences not significant).
- EC alg/crv coupling: ES256 with P-256, ES384 with P-384 enforced
  via if/then in the EC oneOf branch.

overview.md:

- §Profile Structure: the keys[] mirror, the RFC 7517 §5
  "additional members" allowance, and the cross-array equivalence
  rule.
- Business Profile example now publishes both arrays as semantic
  mirrors, with an Ed25519 key (HTTP transport identity) and an
  ECDSA P-256 key (AP2 mandate JWT) demonstrating UCP+WBA+AP2
  composition.
- §Identity & Authentication: tag-driven dispatch language. Sites
  opting into WBA interop emit both UCP-Agent (capability discovery
  + default-UCP key lookup) and Signature-Agent (WBA-shape key
  lookup).
- §Key Discovery: regime-aware lookup table (default UCP →
  signing_keys[]; WBA-shape → keys[]).
- New §Deployment Patterns for WBA Interop subsection: Pattern 1
  (content negotiation, one URL, two media types); Pattern 2 (two
  URLs, isolated signing complexity). WBA Response Signing deferred
  to draft-meunier-http-message-signatures-directory §5.2 with
  operational guidance on authority-keyed caching.
- New §Identity Resolution Algorithm subsection: per-signature
  dispatch on the tag parameter (tag="web-bot-auth" → resolve via
  Signature-Agent; no/other tag → resolve via UCP-Agent; unknown
  tag → skip). Explicit skip semantics for missing Signature-Agent
  header, fetch failure, non-HTTPS URL, no keyid match, and
  signed-components mismatch. SHOULD-level keyid↔JWK-thumbprint
  check for WBA-shape signatures. Authenticated transport-identity
  rule, with explicit scoping that payload-layer assertions (e.g.,
  AP2 mandate JWTs) follow their own identity binding.

signatures.md:

- Architecture diagram updated for keys[] / signing_keys[] dual
  publishing.
- §Key Discovery defers publishing contract to overview.md
  §Profile Structure (regime-aware lookup table only).

Backwards compatibility
-----------------------

Fully additive. Profiles publishing only signing_keys[] (the canonical
field) remain valid. Profiles wanting WBA-directory compatibility add
keys[] as a mirror; the two-array dual-publish keeps every existing
UCP verifier working unchanged.
@igrigorik igrigorik added this to the Working Draft milestone May 27, 2026
@igrigorik igrigorik self-assigned this May 27, 2026
@igrigorik igrigorik added the TC review Ready for TC review label May 27, 2026
@igrigorik igrigorik requested review from a team as code owners May 27, 2026 15:38
Comment thread docs/specification/signatures.md Outdated
Comment thread source/schemas/profile.json Outdated
Comment thread docs/specification/overview.md
@amithanda

Copy link
Copy Markdown
Contributor

Hi @igrigorik, Overall this is a strong, well-scoped PR that solves a real problem: a UCP signer couldn't produce a WBA-conformant signature at all before this, and the PR closes that gap without disturbing existing integrators. A few things I think are genuinely well done: the single-signature dual-audience shape (one key, one signing operation, verified by both regimes off the same bytes) is elegant; backward compatibility holds (ES256 stays the universal baseline, keys[] and the WBA opt-in are both optional).

I left three inline comments, only one of which I'd treat as a pre-merge discussion:

  1. profile.json (the oneOf over closed kty/crv/alg enums): this is the one worth resolving before merge. It narrows previously-open fields into closed enums and makes any future key type or curve fail whole-profile validation, which makes it quite rigid.

  2. signatures.md (the Ed25519 example kid): a should-fix correctness nit, the kid is a real RFC 7638 thumbprint but of the wrong (RSA) key, so the example contradicts the MUST the PR introduces.

  3. overview.md (revocation atomicity): a small but security-relevant clarification, the atomicity guarantee should explicitly cover key removal/revocation across both arrays, since that's the one divergence that fails open. PTAL.

None of these are objections to the direction, which is right.

The WBA-interop examples attached kid
"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs" to the published Ed25519
key, but that value is the RFC 7638 §3.1 thumbprint of the *RSA* example
key. The actual RFC 7638 SHA-256 thumbprint of the published OKP key
{"crv":"Ed25519","kty":"OKP","x":"11qYAY..."} is
"kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k" (RFC 8037 §A.3).

This matters because the WBA opt-in makes keyid = JWK SHA-256 Thumbprint
a MUST and the identity-resolution algorithm checks it: the example
previously failed its own normative requirement and would mislead anyone
lifting it as a test vector. Replaces all four in-tree occurrences
(signatures.md profile example + wire example; overview.md signing_keys[]
and keys[] mirrors).
The Content-Digest in the HTTP examples used the full RFC 9530
"hello world" digest (X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=),
which reads as a real, complete digest but does not hash the body shown
in any example. Truncate to X48E9q... so the placeholder is unambiguously
illustrative, matching the convention already used by the adjacent
Signature lines (MEUCIQD...) and the response example's digest (Y5fK8n...).

Display-only: the example validator unwraps HTTP examples to the JSON
body before validation, so header values are never parsed. The
deterministic, normatively-checked keyid/kid values are intentionally
left as real values.
The keys[]/signing_keys[] mirror creates a dual-removal hazard: a key
removed from one array but still listed in the other keeps verifying for
any verifier reading the stale array. The atomicity rule only mentioned
"rotation updates," and the runbook steps said "remove from the profile"
without naming both arrays — so the security-critical case (revocation)
could fail open.
The PR narrowed the JWK schema's kty/crv/alg into closed enums behind a
oneOf, so a single key with an unrecognized type or curve (a future
P-521 or post-quantum key) failed validation for the entire
signing_keys[]/keys[] array — invalidating the whole profile, including
keys a verifier could use. That re-creates the "can't add an algorithm
without a breaking schema bump" problem this feature exists to remove,
and contradicts the runtime contract (key lookup is by kid, with
algorithm_unsupported for keys a verifier cannot use).

Replace the closed oneOf with open string vocabularies (kty/crv/alg as
examples, not enums) plus allOf if/then conditionals that preserve the
real constraints for known types: EC requires crv/x/y, OKP requires
crv/x, and P-256/P-384/Ed25519 each couple to their algorithm. Unknown
key types now validate (only kid+kty required) instead of bricking the
profile. The private-key guard is unchanged.

Prose updated for coherence:
- signatures.md, overview.md: the kty/crv/alg vocabularies are open and
  verifiers skip keys they cannot use rather than reject the key set.
- Identity-resolution algorithm and REST verify pseudocode now show the
  algorithm_unsupported path for an unsupported matched key.

Verified: ucp-schema lint clean, all doc examples validate, and fixtures
confirm known-bad keys (missing y, P-256+ES384, Ed25519+ES256, missing
x, private key) are still rejected while unknown types/curves pass.
@igrigorik igrigorik requested a review from amithanda June 15, 2026 16:14
@igrigorik

Copy link
Copy Markdown
Contributor Author

@amithanda ty sir, great catch and feedback on the oneOf. Updated, PTAL.

Comment thread docs/specification/signatures.md Outdated
@igrigorik

Copy link
Copy Markdown
Contributor Author

Rereading the Web Bot Auth Architecture [1] and HTTP Message Signatures Directory [2] drafts, directory discovery is ambiguous. The two specs describe it two different ways with no stated precedence, and it affects whether a profile can double as a key directory.

  • Directory §4 [2]: Signature-Agent "contains a URI allowing retrieval of [the] directory" — dereference the URI as-is. Architecture §4.4 [1] agrees ("fetch … as indicated by Signature-Agent").
  • Architecture §4.5 [1]: "The reference for discovery is a FQDN. It SHOULD provide a directory hosted on the well known" — take the host, use /.well-known/http-message-signatures-directory.

Concretely, for Signature-Agent: sig1="https://example.com/.well-known/ucp":

  • a dereference-verbatim verifier fetches …/.well-known/ucp
  • a FQDN + well-known verifier fetches …/.well-known/http-message-signatures-directory

Desired state

One /.well-known/ucp document, carrying one JWK Set, that's simultaneously a UCP profile and a Web Bot Auth directory — discoverable via Signature-Agent — instead of maintaining a separate /.well-known/http-message-signatures-directory. The verbatim-deref reading makes that clean; the FQDN reading forces a second endpoint. What we need to clarify...

  1. Can Signature-Agent point at an arbitrary URL?
  2. If yes: §3 [2] requires application/http-message-signatures-directory+json. Can this be relaxed to ease implementation and allow fused directory / profile we're after.

[1] https://datatracker.ietf.org/doc/html/draft-meunier-web-bot-auth-architecture-05
[2] https://datatracker.ietf.org/doc/html/draft-meunier-http-message-signatures-directory-05

@thibmeu thibmeu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good overall. I think there is opportunity to align the two specifications

  1. Add examples for ecdsa-p256-sha256 in web bot auth specification, with an associated implementation
  2. Have a CIMD shape, specifically with jwks_uri. This would allow both WBA and UCP to keep their own namespace ("ucp" and "web-bot-auth"), and have key management to be decorrelated. For WBA, the changes are being proposed thibmeu/http-message-signatures-directory#98

Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md Outdated
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md
Comment thread docs/specification/overview.md Outdated
Comment thread docs/specification/overview.md Outdated
@pjordan

pjordan commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

[This is a quick review to unblock @igrigorik, largely assisted by claude]

Overall

This is a strong, carefully-iterated PR that turns a previously impossible thing — emitting a Web Bot Auth-conformant signature from a UCP signer — into a clean, backward-compatible capability. I'm in favor of the direction, and after the 06-15 fixes the substrate is in good shape. My position is approve after a small set of correctness/interop fixes, with the contested key-publication architecture routed to a tracking issue rather than blocking here.

A few things I want to credit specifically (extending @amithanda's top-level praise rather than repeating it):

  • The per-signature tag-dispatch is exactly the right primitive. One set of signed bytes, two verification regimes selected on tag, "authenticated if at least one verifies," with unknown tags skipped — that's the correct reading of RFC 9421 §4.3 (multiple signatures) composed with WBA architecture §4.2.3 (UCP's ucp-agent/idempotency-key/content-digest/content-type ride as permitted "additional components"). It's forward-compatible by construction.
  • The @authority cache-key MUST in WBA Response Signing ("any cache … MUST include the request authority in its cache key … otherwise a cached signature served for Host: a.example will fail under Host: b.example") is a subtle, easy-to-omit correctness rule that the directory draft itself doesn't spell out (it only says "cache the directory contents," directory-05 §5.1). It lines up exactly with the @authority;req-MUST-set self-signature in directory-05 §5.2 and is precisely the trap Pattern 1's "identical body bytes" design invites. Nice catch.
  • A1 landed well (commit 44d1dba): $defs.jwk_public_key is now required: ["kid","kty"], the kty const:"EC" is gone, enums are examples, and an allOf of if/then branches preserves EC-needs-x,y / OKP-needs-x and the per-curve→alg coupling while letting an unknown kty pass. That's the right shape — it avoids re-creating the exact "couldn't add Ed25519 without a schema bump" trap this PR exists to fix.

Security & key lifecycle

A3 is resolved in prose — revocation now correctly states a key "is not effectively revoked until it is absent from both arrays" and that add/rotate/remove "MUST update both arrays atomically." Good. The residual I'd put on the record (not re-litigating A3): that MUST is prose-only and structurally unenforceablesigning_keys[] and keys[] are two independent arrays in profile.json, JSON Schema 2020-12 can't express "keys[] mirrors signing_keys[]," and per the Identity Resolution Algorithm each signature reads exactly one array, so no verifier in the resolution path can detect drift either. A compromised key dropped from signing_keys[] but lingering in keys[] keeps verifying for every WBA-shape verifier. I wouldn't bolt a cross-array check onto verifiers (it doesn't reach the regime that fails open) — this is the sharpest standalone argument for @thibmeu's single-list direction, and I'd feed it into that discussion rather than block.

Two net-new security items worth resolving before merge: (1) nonce is absent entirely — the WBA opt-in list omits it, though WBA architecture §4.2.2 says agents SHOULD add a nonce (base64url, ~64 bytes, unique within the validity window) and origins MAY require one (§4.4); a UCP signer following our 6-item list to the letter is nonce-less and a strict WBA origin is entitled to re-challenge it (§4.3). UCP's idempotency-key is business-layer dedup, not a transport-bound nonce, so it isn't a substitute. (2) A freshness contradiction: the WBA section says RFC 9421-conformant verifiers "will enforce the freshness window," while the standing note still says created is OPTIONAL and "UCP handles replay protection at the business layer … not signature timestamps." These are reconcilable by scoping (WBA-shape MUSTs created/expires; default UCP doesn't), but as written a reader can't tell which governs. (Minor precision: RFC 9421 §3.2.1 makes expiry/nonce enforcement an application-defined requirement a verifier MUST then enforce — it isn't an automatic consequence of conformance — so "UCP verifiers MUST reject expired WBA-shape signatures" is the accurate phrasing.)

Interop reality

This is the item I'd weight most heavily, and it sharpens @dpeacock's Cloudflare comment and @igrigorik's discovery-precedence question from open-questions into a concrete interop break. I tried to mentally run Pattern 1 (and the worked examples that point Signature-Agent at …/.well-known/ucp) against the one WBA verifier shipping today, Cloudflare, and I don't think it verifies. Cloudflare's docs make the directory at /.well-known/http-message-signatures-directory a hard requirement, require Content-Type: application/http-message-signatures-directory+json, and require per-key response self-signatures; its signed-request example carries a bare origin Signature-Agent, which (the only reading consistent with both facts) means it takes the authority and appends the well-known path rather than dereferencing the URL verbatim. So Pattern 1 fails on path, media type, and self-signature simultaneously. To be fair to the PR: the verbatim-deref reading is spec-legal under directory-05 §4.1 — this isn't "wrong per the draft," it's "incompatible with the deployed §4.5-reading verifier." Pattern 2 already provisions a real signed directory endpoint and is the conformant path. I'd resolve this before merge by either (a) making Pattern 2 (bare-origin Signature-Agent, the directory media type, per-key self-sigs) the WBA-conformant default and scoping Pattern 1 as "spec-legal per directory-05 §4.1 but not interoperable with deployed verifiers today," or (b) holding Pattern 1 until the directory draft adds an explicit precedence rule. Relatedly: the PR tells verifiers to "discard keys without a valid self-signature" (IRA step 4) but never names the concrete contract to do so — the directory self-signature uses tag="http-message-signatures-directory", distinct from the request tag="web-bot-auth", and that string appears 0× in the diff. Two sentences naming the tag, the @authority;req component, and the +json media type would make step 4 actionable.

Architecture & future-proofing

The central choice — inline keys[] mirror vs @thibmeu's jwks_uri/CIMD direction (thibmeu/http-message-signatures-directory#98) — is currently being made implicitly. I don't think the PR should pivot; shipping the mirror now to unblock dual-audience is reasonable and lower-cost. But I'd like @igrigorik and @thibmeu to put the decision on the record: is keys[] a true mirror we intend to deprecate in favor of a jwks_uri/type=cimd shape, or a permanent canonical field? The migration seam matters — the PR twice promises "a future UCP version may promote keys[] to canonical" but the IRA step 3 hard-binds "default UCP signature → read signing_keys[]," so a later deprecation is a breaking change unless we write the fallback rule (e.g., "verifiers SHOULD fall back to keys[] when signing_keys[] is absent") now. I'd route convergence to a dedicated issue alongside the discovery-precedence thread. Two smaller future-proofing notes: the tag namespace UCP is establishing as a routing key is an un-registered free-form RFC 9421 §2.3 string (worth stating how UCP allocates the values it recognizes, and considering reserving tag="ucp" while still accepting untagged-as-UCP for backcompat); and the normative coupling to two individual, non-WG-adopted drafts (architecture = Informational, directory = Standards Track, both "Expires 3 September 2026") should be pinned to -05 per @thibmeu's T6 — all five draft-meunier-* links are currently bare.

Adoption & governance

Two cheap, high-leverage fixes: the PR description still carries the RSA thumbprint NzbLsXh8… on Ed25519 keys (3×) — the spec files are fixed to kPrK_…, the body just needs the same swap so the canonical example doesn't fail its own item-4 MUST (extending @amithanda's A2, which asked for the body fix too). And per @thibmeu's T3, one fully-worked vector reusing the named RFC 9421 test-key-ed25519 (x=JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs, thumbprint poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U) would make test material recognizable as non-prod. On the AP2 two-key story: it's accurate and honestly labeled interim (AP2 v0.2 §Payment Mandate does hard-MUST exclude Ed25519 for the Checkout JWT). I'd add two calibrations — that the single-key future is gated on AP2 #268, which is OPEN, author-filed, and heading to the FIDO Agentic Payments WG (a strength worth stating plainly, not concealing), and that the actual fix is a payload-entropy rule UCP satisfies via the Checkout id, not algorithm-permissiveness (plain Ed25519, including ph/ctx, contributes zero signature entropy). On ecdsa-p256-sha256 (T4): I read architecture-05 §4.2 as permitting any IANA-registered algorithm via an open set rather than naming ES256 specifically (the draft ships vectors only for rsa-pss-sha512 and ed25519), and Cloudflare accepts Ed25519 only today — so "spec-permitted via the open set, not deployed" is the precise framing, and the Ed25519 SHOULD here is right.

Merge recommendation: approve-after-changes. Land the baseline; gate merge on the cheap correctness/interop items (PR-body thumbprint, nonce SHOULD + the freshness scoping, the directory self-sig tag, the Pattern-1 conformance caveat, -05 pinning, one worked vector). Route the discovery-precedence decision and the inline-mirror-vs-jwks_uri fork to a tracking issue for @igrigorik / @thibmeu / @amithanda to converge — and strongly consider splitting the uncontested Ed25519/OKP schema fix into its own merge so the ecosystem can adopt the unblock immediately. None of this is an objection to the design, which is the right one. Thanks for the careful iteration — and for filing AP2 #268 upstream rather than waiting on it. 🚀

Comment thread docs/specification/overview.md
Comment thread docs/specification/signatures.md
Comment thread docs/specification/signatures.md
Comment thread docs/specification/signatures.md Outdated
@shopdave

Copy link
Copy Markdown
Contributor

Regarding @pjordan's remarks on interop reality: I do think there is something to address here. Adding something like:

Publishing top-level keys[] is necessary but not sufficient for UCP-WBA interop. A profile or directory endpoint used for WBA key discovery MUST be served with per-key HTTP Message Signatures on the response; otherwise WBA verifiers following the directory draft’s recommended validation behavior will ignore the keys.

or something to that effect may resolve it.

   Adopt the explicit `type` parameter for Signature-Agent so a verifier no
   longer has to guess whether the value is a directory origin or a JWK Set
   URL. UCP recommends `type=jwks_uri` pointing at the UCP profile: the
   profile's keys[] array is read directly as the JWK Set, so one
   /.well-known/ucp document is simultaneously the UCP profile and the WBA
   key source — no separate directory, no content negotiation, no directory
   media type.

   - signatures.md: WBA opt-in step 2, Signature-Agent parsing rules, and
     both examples now carry ;type=jwks_uri; post-example prose explains the
     single-URL discovery.
   - overview.md: Deployment Patterns reframed — Pattern 1 (profile as JWK
     Set via jwks_uri, recommended) and Pattern 2 (well-known directory,
     fallback for default type=directory verifiers such as Cloudflare
     today); WBA Response Signing scoped to the Pattern 2 directory;
     identity-resolution algorithm resolves by type.

   Tracks the `type` parameter proposed in
   draft-meunier-http-message-signatures-directory PR #98
   (github.com/thibmeu/http-message-signatures-directory/pull/98), still in
   flight; the well-known directory (Pattern 2) remains for interop with
   verifiers that have not adopted typed discovery.
…pen thumbprint prose

Review nits on PR #483:

- Swap the OKP example key to RFC 9421 B.1.4 test-key-ed25519
  (x=JrQLj5P..., thumbprint poqkLGiymh...) so UCP examples cross-check
  directly against Web Bot Auth's own test vectors (architecture A.2) and
  read as recognizable non-prod material. (thibmeu, pjordan)
- Pin all five draft-meunier-* references to -05 (display text + URL);
  these individual drafts evolve. (thibmeu, pjordan)
- Cross-array equivalence MUST now cites the base64url JWK SHA-256
  Thumbprint per RFC 7638 Section 3.2 (RSA/EC) and RFC 8037 Appendix A.3
  (Ed25519). (thibmeu)
- cspell: ignore kid/keyid thumbprint values (opaque base64url, nothing
  spell-checkable), mirroring the existing x/y JWK-coordinate ignore.
…ed-component gate

Two coupled corrections so the verification logic nests correctly and
Web Bot Auth stays an optional, additive layer over a self-sufficient
base mechanism.

1. Key resolution is verifier-capability-based, not tag-gated. The
   previous algorithm dispatched on the signature's tag
   (tag="web-bot-auth" -> Signature-Agent only), which made WBA
   load-bearing for any tagged signature and left a UCP-only verifier
   no UCP-Agent path for a dual-audience signature. Now UCP-Agent ->
   signing_keys[] is the base lookup every UCP verifier supports
   (untagged or tag="web-bot-auth" signatures); Signature-Agent ->
   keys[]/jwks_uri is the optional lookup a WBA-aware verifier MAY use.
   The tag is a hint, not a gate; signatures scoped to another
   application's purpose are skipped. A dual-audience signature thus
   verifies for a UCP-only verifier (via UCP-Agent), a WBA-only verifier
   (via Signature-Agent), or both. Key-list selection follows the
   resolution mechanism.

2. Covered-component requirements are enforced at the verifier, in every
   regime and transport. The verifier previously accepted a signature
   regardless of what it covered, so a signature satisfying only WBA's
   minimal set (@authority, signature-agent) could authenticate a
   request whose body/method/path is unbound. Add a presence-based
   coverage gate: bind the target always, the body when present, and
   every integrity-relevant header the request carries (ucp-agent,
   signature-agent, idempotency-key). Presence-based, never branching on
   HTTP method, so it holds for MCP (all POST) as for REST. Which
   requests MUST carry Idempotency-Key remains a transport-binding
   presence rule (REST: method; MCP: operation), separate from coverage.

Updated: Identity Resolution Algorithm (resolution, key-list selection,
coverage gate), Key Discovery prose (both files), the WBA-opt-in note,
both REST verify pseudocode blocks, and the MCP Transport note
(Idempotency-Key follows the operation, not the POST).

Raised in review on PR #483: WBA must be optional, not required;
covered-component gap reported by shopdave; MCP all-POST scoping caught
in review.
   Two review items (PR #483) on WBA-interop signature semantics:

   - nonce: the WBA opt-in list omitted the anti-replay nonce. Add it as a
     SHOULD — base64url, RECOMMENDED 64 bytes, unique within the
     created/expires window — per
     draft-meunier-web-bot-auth-architecture-05 §4.2.2, noting that UCP's
     Idempotency-Key is business-layer payload deduplication, not a
     transport-bound nonce and not a substitute, and that a verifying
     origin MAY require a nonce and re-challenge (HTTP 429) per §4.3-4.4.

   - freshness: the spec both implied RFC 9421 conformance "will enforce"
     the created/expires window and stated created is OPTIONAL with replay
     handled at the business layer — a contradiction, and the "will
     enforce by conformance" was inaccurate. Scope it by regime: default
     UCP keeps created OPTIONAL with Idempotency-Key replay protection;
     WBA-shape signatures carry created/expires (required of the signer by
     WBA §4.2) for WBA verifiers. Enforcing the window is
     application-defined (RFC 9421 §3.2.1) — not an automatic consequence
     of conformance and not separately mandated by UCP.
   Reframes optional Web Bot Auth (WBA) interop around three pivots, then
   closes the correctness/security gaps a review surfaced.

   Pivots
   ------
   - UCP does not become a WBA directory. The profile is a valid RFC 7517
     JWK Set a signer MAY reuse as its key source; WBA discovery happens via
     the Signature-Agent header's type variants (jwks_uri / cimd / directory),
     each able to point back at the profile.
   - Spec the minimum interop surface; defer mechanics to their source specs.
     The in-spec directory self-signature contract ("WBA Response Signing")
     and the IRA directory-resolution step are removed — that format, its
     per-key self-signatures, and media type belong to the directory draft
     (§5.2), not UCP. UCP stops restating RFC 9421 / the WBA drafts / CIMD.
   - The three discovery variants are equals. UCP does not rank them; the
     right type depends on the counterparty's verifier. The type parameter
     is flagged as tracking the unmerged directory-draft PR #98 (may change).

   Correctness
   -----------
   - Define "dual-audience" and require kid == RFC 7638 thumbprint for
     dual-audience keys (schema + prose). Otherwise the same signature a WBA
     verifier accepts fails UCP-Agent resolution: keyid is the thumbprint and
     the UCP verifier matches keyid to a published kid, so both must be it.
   - Verifier algorithm: close the covered-component gate to an explicit set
     (no open "every integrity-relevant header"); C4 keyid==thumbprint check
     SHOULD -> MUST for tag="web-bot-auth"; tighten identity binding (a
     Signature-Agent key proves control of that key source, not of the
     UCP-Agent profile — treat as one identity only when the URLs match);
     note that verifying a dual-audience signature still needs Ed25519 and
     RFC 9421 §2.1.2 member selection.
   - Fetch safety (the directory draft is silent, so this is UCP's): reject
     special-use IPs (RFC 6890) with a loopback carve-out for local dev,
     resist DNS rebinding, do not follow in-document URLs to internal
     addresses, and bound response size (128 KiB deployment guard — the
     schema sets no size limit, so it MUST NOT reject conformant profiles).
     Rules apply to every identity-resolution dereference. Cache-refresh on
     kid-miss MAY -> SHOULD.
   Merges origin/main, resolving the docs/specification/signatures.md conflict
   with #291 (footnote rendering + MD060 table alignment) by keeping this
   branch's WBA content and adopting main's backtick-footnote + aligned-table
   convention. Also carries the WBA-interop alignment and clarity pass below
   (folded in via amend).

   Align with merged directory PR #98
   ----------------------------------
   thibmeu/http-message-signatures-directory#98 merged, confirming our
   forward-leaning bets: the Signature-Agent `type` parameter
   (jwks_uri / cimd / directory, default directory) and the §5.2 key-binding
   contract are now in the directory draft's editor's copy (pending -06; not in
   published -05). Updated the citation from "PR #98, unmerged, may change" to
   the draft itself. Verified the architecture-draft §4.2 agent-signature
   requirements citation still holds.

   Clarity / de-conflation
   -----------------------
   - The UCP profile is a valid RFC 7517 JWK Set a signer reuses as its key
     SOURCE — not a "key directory". Reserve "directory" for the WBA well-known
     mechanism only. Fixed across the profile prose, the Authenticated-identity
     rule, the signatures heading, the Signature-Agent table cells, the REST
     verification pseudocode (resolve_signer_key_set), and profile.json.
   - keys[] -> canonical is "WILL" (not "may"), stated with the valid-JWKS
     motivation; signing_keys[] is the deprecation target.
   - Moved the dual-audience example to the top of the WBA Interop section with
     a coexistence annotation: Signature-Agent is additive alongside UCP-Agent,
     it does not replace it.
   - DRY'd the IRA ("tag is a hint, not a gate" stated once); dropped the stale
     tag argument from the verifier pseudocode; aligned profile.json key
     descriptions (jwks_uri-scoped verifier note, thumbprint-precise equivalence,
     signer-scoped EdDSA recommendation).
@igrigorik

igrigorik commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Ty all for thorough feedback. Pushed a major revision, updated PR description to match. Key changes:

1/ UCP doesn't become a WBA directory.
The profile is a valid RFC 7517 JWK Set a signer MAY reuse; WBA discovery is just Signature-Agent's typed variants (jwks_uri/cimd/directory), presented as options, each able to point back at the profile. The in-spec directory self-signature contract is removed — format/self-sigs/media-type are deferred to the directory draft §5.2. This reverses the earlier "name the self-sig contract" direction: that mechanism belongs to the draft, not UCP.

2/ A UCP verifier behaves the same whether or not a signature is WBA-shaped.
It always finds the signing key via UCP-Agent, regardless of the signature's tag — so WBA support stays optional and a UCP-only verifier needs zero WBA-specific code. Separately, every signature (WBA-shaped or not) MUST cover the same required request parts (method, path, body digest, identity headers); one that covers only WBA's bare minimum can't authenticate a UCP request whose body or path it left unsigned.

3/ "Dual-audience" is now defined and scoped.
kid MUST equal the RFC 7638 thumbprint for dual-audience keys (else the same signature a WBA verifier accepts fails UCP-Agent resolution), the keyid==thumbprint check is MUST, and fetch safety (RFC 6890 special-use-IP rejection + loopback carve-out + response-size bound) is specified — aligned with the WBA architecture draft's SSRF guidance.

PR #98 landed – hooray! @thibmeu I filed thibmeu/http-message-signatures-directory#100: would love to remove unnecessary friction here, wdyt?

Ready for next review pass — ptal 🙏

p.s. this PR introduces keys[] as parallel option, which keeps this PR backward compatible. Having done a few more turns on this proposal, I'm conviced we should make keys[] the default but I'll stage that as separate PR that we can fast-land after we land this one -- we can backport this change and make keys[] default as breaking change in next (08) release.

The Business Profile example's ECDSA P-256 key used fabricated coordinates.
Swap to the RFC 9421 Appendix B.1.3 key (test-key-ecc-p256) so both example
keys are recognizable RFC 9421 test keys (the Ed25519 key already is), per
reviewer feedback to reuse RFC 9421 test keys for ease of verification.
…ndates

   The algorithm/key guidance stated specific prescriptions tied to today's
   deployment — Ed25519 for WBA, "two keys" for WBA+AP2 — as if they were
   UCP/WBA rules. Generalize to the underlying principle: algorithm choice is
   counterparty-driven. The guidance then holds as verifier capabilities (or
   AP2's rule) change, without naming a specific algorithm or key count.

   signatures.md (§Signature Algorithms)
   - Usage guidance is now principle-first: algorithm choice is
     counterparty-driven; default to ES256 (the UCP baseline, also WBA-valid);
     one key suffices when one algorithm satisfies every audience.
   - Ed25519 and the two-key setup demoted to a non-normative observation.
   - "WBA verifiers MUST support EdDSA" → SHOULD (soft floor); WBA opt-in item 1
     "SHOULD be Ed25519" → "use an algorithm the verifier accepts"; IRA step 1
     generalized ("Ed25519" → "whichever algorithm the signer used"); architecture
     box de-WBA'd. "Two-key vs one-key" → "Number of signing keys".
   - De-duplicate: the RFC 9421 HSA registry list now appears once (WBA opt-in);
     drop the redundant "does not mandate a specific one" and the "Cloudflare"
     vendor name.

   ap2-mandates.md (§Cryptographic Requirements) — now the home for AP2 algorithm detail
   - The AP2 bullet in §Signature Algorithms is now a pointer here.
   - Defer the algorithm to AP2's rule (AP2 v0.2 requires ECDSA) and add a Note
     covering the entropy basis (UCP's per-session Checkout id supplies it), AP2
     v0.2's specification.md-vs-S&P contradiction, AP2 #268, and that under the
     entropy reading one key (incl. Ed25519) serves both AP2 and WBA.
   - Reconcile the alg field table and verify pseudocode to defer rather than
     hard-code ES256/ES384/ES512.

   overview.md
   - IRA step 1 generalized; business-profile caption defers the one-vs-two-key
     rule to §Signature Algorithms (no longer cites AP2's non-deterministic rule).

@thibmeu thibmeu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to the specification make it much closer to WBA, specifically with the optional keys.

The addition of Signature-Agent with directory/jwks_uri/cimd helps a lot, and reuses discovery via signatures components.

Only question mark I'd have is regarding using keys rather than jwks_uri, but that's a recommendation that can evolve later, and that's valid today

Comment thread source/schemas/profile.json
Comment thread docs/specification/overview.md Outdated
selects how a verifier resolves the advertised keys. (The `type`
parameter and its `jwks_uri`/`cimd`/`directory` values were added via
[directory-draft PR #98](https://github.com/thibmeu/http-message-signatures-directory/pull/98);
they are in the editor's copy, pending publication as -06.) Each

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thibmeu that's an item on me. Should be this week

Comment thread docs/specification/overview.md Outdated
Comment thread docs/specification/overview.md Outdated
@pjordan

pjordan commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

[Second pass, post the 06-19 revision. Largely assisted by Claude, with the external claims checked against primary sources by Claude.]

I am approve ready.

Claude's review

The 06-19 revision is a big step and it resolved most of what I — and @amithanda, @shopdave, @thibmeu — raised last round. Crediting the changes specifically so we don't re-litigate them: nonce is now a SHOULD with the WBA §4.2.2 shape (~64-byte base64url, unique within the validity window) and the "Idempotency-Key is not a nonce" distinction is explicit; the freshness contradiction is resolved by scoping (created/expires MUST for WBA-shape, OPTIONAL for default UCP) with the correct "enforcement is application-defined per RFC 9421 §3.2.1" framing; the AP2 two-key story moved to ap2-mandates.md and is accurately calibrated (entropy-vs-algorithm-class, AP2 #268, UCP's per-session Checkout id as the entropy source); ecdsa-p256-sha256 is framed as permitted-via-open-registry; the architecture refs are pinned to -05; and the Ed25519 example kid is now the correct RFC 7638 thumbprint of the RFC 9421 test key (poqkLGiymh…, which I recomputed — it matches, and the old RSA-thumbprint value is gone from both the spec and the PR body). The capability-based Identity Resolution Algorithm (tag-as-hint, not gate), the closed covered-component gate (@shopdave's), the private-key not-guard in profile.json, the open kty/crv/alg vocabulary, and the new "Authenticated identity" section closing the Signature-Agent-vs-UCP-Agent identity-confusion gap are all the right calls. Net: I'm at approve-after-changes, and the list is now short.

One thing I checked that turned out fine: the type=directory semantics. Against the merged directory PR #98 (commit 40119d8), type=directory does mean "member value is an origin, append the well-known path," while jwks_uri/cimd are verbatim-deref — so the PR's description is correct, not a bug. That verification surfaced the items below.

@thibmeu thibmeu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me!

@igrigorik

Copy link
Copy Markdown
Contributor Author

@thibmeu ty for your help and feedback!

@pjordan clarification on keys[]: current PR adds this object as optional and parallel to existing signing_keys[], with suggestion to mirror same keys in both -- this shape will land as backwards compatible via this PR. After this lands, I'll stage a separate PR to make keys[] the default and remove signing_keys[] -- this will eliminate duplication and make UCP profile a valid JWKS; this will go into next major release as breaking change.

That aside, I believe this is good land. Ready for final pass.

igrigorik and others added 3 commits June 25, 2026 22:27
Co-authored-by: Thibault <thibault@cloudflare.com>
   Point the Signature-Agent `type` parameter at draft-meunier §4.1
   (to be published in -06) via the IETF datatracker htmlized draft,
   replacing the now-merged/closed PR #98 link and the github.io
   editor's-copy link. Reflow thibmeu's tag-skip sentence to the
   file's line-wrap width.

@shopdave shopdave left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with minor corrections.

Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/overview.md
Comment thread .cspell/custom-words.txt Outdated
Comment thread docs/specification/signatures.md
@amithanda

Copy link
Copy Markdown
Contributor

Hi @igrigorik,

+1 to addressing @shopdave's suggestions few hours ago. I wanted to flag a concern regarding the normative coupling to the unreleased -06 version of the key directory draft.

The Concern: Spec Drift & Out-of-Date References

In docs/specification/overview.md (around L1275-1300), we define the Signature-Agent type parameters (jwks_uri, cimd, and directory):

  • These parameters are introduced to UCP in this PR to support Pattern 1 (type=jwks_uri pointing to the UCP profile).
  • While I see that draft references were pinned to -05 in a recent commit to align with prior feedback, these parameters are not actually present in the currently published draft-meunier-http-message-signatures-directory-05. As noted in the PR description, they are in the editor's copy, pending publication as -06.
  • Currently, pinning the UCP spec text to a Datatracker URL that resolves to -05 means a developer reading the official document will not find these parameters, leading to validation confusion.
  • More importantly, because these parameters are part of an active IETF working group (webbotauth) draft that is not yet finalized, there is a risk that the working group could rename, restructure, or drop these parameters before -06 or the final RFC is published. If that happens, UCP's wire format will drift from the standard. I would like to hear your thoughts on this.

Suggestion: I completely appreciate the goal to keep UCP isolated from WBA directory internals. However, to protect early implementers from spec drift before the final RFC lands, could we either temporarily point the Markdown links directly to a commit-locked version of the editor's copy where they are defined, or add a brief informative footnote indicating that these parameters track the upcoming -06 working group evolution?

Single-Array Migration

The security gaps @shopdave highlighted in the verify_rest_request() pseudocode—where the logic completely misses the keyid == thumbprint validation step—perfectly underscores why the parallel array structure represents an architectural risk, so I fully endorse the resolution plan (in response to @pjordan's comment) in your status note to promote keys[] as the default source of truth very quickly after this.

Co-authored-by: David Pettersson <david.pettersson@shopify.com>

@thibmeu thibmeu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the latest editor copy of the webobotauth drafts have been published on the datatracker. there has been name changes (based on feedback from the wg), which i've added here, in addition to pinning the versions

Comment thread docs/specification/overview.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/signatures.md Outdated
Comment thread docs/specification/overview.md Outdated
   - REST verify pseudocode now consistently skip-and-try-next: remaining
     hard error(...) returns (request digest; all of verify_rest_response)
     become skip_signature(...), plus a note that each routine verifies a
     single candidate — authenticated iff one verifies, rejected only when
     every candidate skips (RFC 9421 §4.3 / IRA).
   - overview: caveat that a WBA verifier reads keys[] only via
     type=jwks_uri/cimd; default type=directory expects a signed directory,
     not a static profile.
   - signatures: add "interop is directional" callout (UCP verifier ⊂ WBA).
   - cspell: dedup custom-words.txt.
   The WBA drafts were renamed/re-adopted:
     http-message-signatures-directory -> webbotauth-httpsig-directory
     web-bot-auth-architecture          -> webbotauth-httpsig-protocol

   Update all citations to the new slugs and pin -00 — the published
   version that now carries the Signature-Agent `type` parameter at §4.1,
   resolving the "params not in published draft" concern. Fix section
   numbers that shifted in the protocol draft: nonce §4.2.2 -> §4.2.3,
   additional components §4.2.3 -> §4.2.4 (§4.2 unchanged). Keep the
   well-known path and media type, which the new draft retains.
@igrigorik

Copy link
Copy Markdown
Contributor Author

@thibmeu ty, landed updates with a few additional section pointer fixes.

@amithanda prescient comments!

The type parameter (jwks_uri/cimd/directory) is now in the published -00 of the renamed directory draft, at §4.1 — so a developer following our link lands on a version that actually contains it. I've re-pinned every citation to -00. Since datatracker versions are immutable, this is effectively your option (a): a commit-locked pointer to where the parameters are defined, with no silent drift. I've also updated the PR description.

Re, single-array migration: 👍 and I'll stage that as soon as we land this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants