docs: add RFC 9421 request signing guide#914
Conversation
|
Nice guide — picking this up after a walk-through with the existing signing helpers. Two ergonomic improvements would cut Step 4 almost in half without changing the conceptual framing. 1.
|
- Replace requireSignatureWhenPresent manual composition with requireAuthenticatedOrSigned in Step 4 and build-an-agent.mdx — the higher-level helper also enforces request_signature_required for the unsigned-no-credentials path - Use mcpToolNameResolver instead of inline JSON-RPC resolver boilerplate throughout - Fix resolveOperation in the Express middleware example (was incorrectly using req.body.method) - Add conformance vector counts: 39 total (12 positive, 27 negative) Addresses feedback from adcontextprotocol/adcp-client#914 comment by bokelley; tracks mcpToolNameResolver landing in adcp-client#916.
|
Quick update — the SDK ergonomics that motivated some of the early review comments here have shipped on
The canonical home for the docs is now adcontextprotocol/adcp#3064 — once that merges and a release of Closing this one out makes sense at this point unless you want to keep it open for cross-reference. |
…-AGENT Adds docs/guides/SIGNING-GUIDE.md covering the full RFC 9421 signing workflow: key generation, JWKS/brand.json publication, client-side signing, server-side verification, webhook signing, capability declaration, key rotation, and conformance testing. Updates README.md to link the guide from the AI Agents section and rewrites the Security > Request Signing section with better orientation. Adds a Request Signing section to BUILD-AN-AGENT.md showing requireSignatureWhenPresent composition and webhook signer config.
…edOrSigned Apply the review feedback on #914 against the SDK ergonomics shipped in #916 (mcpToolNameResolver) and #917 (defaults + createAgentSignedFetch preset): SIGNING-GUIDE.md - Step 3 (buyer-side): lead with createAgentSignedFetch one-call preset for the single-seller case; keep buildAgentSigningFetch as the multi-seller fallback. Drops the verbose CapabilityCache wiring from the headline example. - Step 4 (seller-side, Express middleware): drop explicit InMemoryReplayStore / InMemoryRevocationStore (now default per #917). Use mcpToolNameResolver instead of the inlined JSON-RPC parser. Add a one-liner about swapping in shared stores for horizontally scaled fleets. - Step 4 (composing with bearer auth): replace the three-variable manual composition (signatureAuth / bearerAuth / requireSignatureWhenPresent) with requireAuthenticatedOrSigned in one call. Now also enforces the spec's request_signature_required 401 via requiredFor: [...MUTATING_TASKS] — previously absent from the example. BUILD-AN-AGENT.md - Mirror the requireAuthenticatedOrSigned + mcpToolNameResolver + MUTATING_TASKS pattern in the Request Signing snippet so the two guides agree. Functional surface unchanged; the manual composition still works for callers that need finer control. The recommended path is just shorter.
cf640a7 to
e7e1d25
Compare
Six must-fix items raised by code-reviewer + ad-tech-protocol-expert
on the post-polish state:
1. MUTATING_TASKS imports were wrong — only @adcp/client (root)
exports it, not @adcp/client/server. Buyer copies would have
compile-errored. Split the imports.
2. Capability override key is `request_signing`, not
`signed_requests`. Wrong key gets silently dropped — verifier
wires up but get_adcp_capabilities advertises nothing, so
buyers don't sign. Fixed in both guides; add a clarifying
sentence so the trap is visible.
3. Widen `mcpToolNameResolver` parameter type from
`IncomingMessage & { rawBody?: string }` to `{ rawBody?: string }`.
Function only reads rawBody, so widening lets it satisfy both
the `verifySignatureAsAuthenticator` (IncomingMessage-shaped)
and `createExpressVerifier` (ExpressLike-shaped) call sites
without casts. No runtime change.
4. Error code table was wrong — `missing_signature` /
`invalid_signature` etc. don't exist. The real codes
(cross-checked against compliance/cache/3.0.0/test-vectors/
request-signing/negative/) are `request_signature_*`. Replace
the seven-row table with the full 15-row table from the vectors
and note that they're a separate signature-error namespace
surfaced via WWW-Authenticate (not entries in
enums/error-code.json).
5. Signature coverage misstated. content-digest is conditional
on covers_content_digest, not always covered. Reword.
6. brand.json shape was wrong. agents[] requires {type, url, id};
`capabilities` and `adcp_use` arrays don't exist in
schemas/cache/3.0.0/brand.json. Fix the example, list the
`type` enum.
Also reframed the "mandatory in 3.1+" claim to "4.0,
spend-committing operations" per
schemas/cache/3.0.0/protocol/get-adcp-capabilities-response.json,
and softened `requiredFor: [...MUTATING_TASKS]` to a narrow
example with MUTATING_TASKS as the upper bound (the spec stance
in 3.0 is "empty by default; populate selectively per
counterparty").
Conformance vector counts (12 positive + 27 negative = 39)
verified against the disk layout.
|
Cross-SDK update on the ergonomics work this PR's review surfaced:
All three carry the same security-by-default replay-store default and the same single-seller buyer preset shape (idiomatic to each language). Canonical docs PR is adcp#3064 — I left a cross-SDK status comment there. |
Summary
docs/guides/SIGNING-GUIDE.md— a step-by-step guide covering the full RFC 9421 signing workflow: key generation, JWKS/brand.json publication, client-side signing, server-side verification, webhook signing, capability declaration, key rotation, and conformance testing.README.mdto link the guide from the "For AI Agents" section and rewrites the Security > Request Signing section with better conceptual framing before code examples.BUILD-AN-AGENT.mdshowingverifySignatureAsAuthenticator+requireSignatureWhenPresentcomposition and webhook signer config.Test plan
@adcp/clientAPI surface (verified againstsrc/lib/signing/andsrc/lib/server/auth-signature.ts)