Skip to content

fix(rest): verify dual-audience (WBA-shape) request signatures - #279

Open
SergeevDmitry wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
SergeevDmitry:fix/rest-dual-audience-signatures
Open

SergeevDmitry wants to merge 2 commits into
Universal-Commerce-Protocol:mainfrom
SergeevDmitry:fix/rest-dual-audience-signatures

Conversation

@SergeevDmitry

@SergeevDmitry SergeevDmitry commented Nov 5, 2026 •

Copy link
Copy Markdown

Description

signatures.md recommends a single dual-audience signature for Web Bot Auth interop, one that both a UCP verifier (resolving via UCP-Agent) and a WBA verifier accept (L246-L252). It covers "signature-agent";key="sig1" and carries tag="web-bot-auth" with the key's RFC 7638 thumbprint as keyid. For a UCP verifier the spec says the key parameter "selects a Dictionary member per RFC 9421 §2.1.2" (L368-L372), and its verification routine requires signature-agent coverage when the header is present and the thumbprint keyid under tag="web-bot-auth" (L693-L698, L717).

Both reference verifiers return no parse result for any component with a parameter (ucp_signing.py L217, signature.ts L170), so the recommended signature is answered signature_missing. The code lists this as a follow-up (L324); this PR is that follow-up, for Python and Node.

The spec's example request, signed with the RFC 9421 test-key-ed25519 key, whose thumbprint is the example's keyid:

Request main this PR
the dual-audience signature 401 signature_missing verifies
same, keyid is not the key's thumbprint 401 signature_missing 401 signature_invalid
same, Signature-Agent present but not covered verifies 401 signature_invalid (coverage)
a second label using "@method";sf next to the valid one 401 signature_missing the valid one verifies

The third row is a coverage gap on main: a signature that leaves the request's Signature-Agent unsigned is accepted.

Changes, the same in rest/python/server/ucp_signing.py and rest/nodejs/src/utils/signature.ts:

  • The Signature-Input parser keeps each component's key parameter. A component with any other parameter marks only its own signature unsupported, which is then skipped like any other failing candidate (RFC 9421 Section 4.3) instead of failing the whole header.
  • A covered (name, key) resolves to that Dictionary member's value, re-serialized without optional whitespace, as RFC 9421 Section 2.1.2 defines it (dictionary_member / dictionaryMember).
  • required_components / requiredComponents require signature-agent when the header is present.
  • Under tag="web-bot-auth", keyid must equal the RFC 7638 thumbprint of the resolved JWK (jwk_thumbprint / jwkThumbprint).
  • The signature base writes "signature-agent";key="sig1" for such a component. buildSignatureBase in Node is generic over the component type, so existing callers are unchanged.

created and expires are not enforced; the spec leaves that to the application (L374-L379).

Testing:

  • New tests in ucp_signing_test.py and test/signing.test.ts: RFC 7638 vectors (RFC 8037 A.3 and the spec's keyid), Dictionary member serialization, key parameter parsing, and the four rows above against a signature base built by hand in the test.
  • Python (3.14): pytest 366 passed; ruff check and ruff format --check clean (ruff 0.17.3). The new tests fail on main.
  • Node (20.17): npm run build (tsc, which also type-checks the tests) and npm test 182 passed; prettier@4.0.0-alpha.8 --check clean. On main the test file does not load, since the new helpers do not exist there; run against the main verifier, the dual-audience and ;sf cases answer signature_missing.

Category (Required)

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Governance/Contributing: Updates to GOVERNANCE.md, CONTRIBUTING.md, or CODEOWNERS. (Requires Governance Council approval)
  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)
  • Infrastructure: CI/CD, Linters, or build scripts. (Requires DevOps Maintainer approval)
  • Maintenance: Version bumps, lockfile updates, or minor bug fixes. (Requires DevOps Maintainer approval)
  • SDK: Language-specific SDK updates and releases. (Requires DevOps Maintainer approval)
  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)
  • UCP Schema: Changes to the ucp-schema tool (resolver, linter, validator). (Requires Maintainer approval)
  • Community Health (.github): Updates to templates, workflows, or org-level configs. (Requires DevOps Maintainer approval)

Related Issues

None.

Checklist

  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk.

signatures.md recommends one dual-audience signature for Web Bot Auth
interop and says a UCP verifier accepts it. That signature covers
"signature-agent";key="sig1" and carries tag="web-bot-auth" with the
key's RFC 7638 thumbprint as keyid. parse_signature_input returned None
for any component with a parameter, so such a request was answered
signature_missing.

Select the Dictionary member a key parameter names (RFC 9421 Section
2.1.2), require signature-agent coverage when the header is present,
and under tag="web-bot-auth" require keyid to be the key's thumbprint,
as the verification routine in signatures.md does. A component
parameter other than key now skips only that signature, not the whole
header.
The Node verifier had the same gap as the Python one: a component with
a parameter made parseSignatureInput return null, so the dual-audience
signature signatures.md recommends was answered signature_missing.

Mirror the Python change: Dictionary member selection for the key
parameter (RFC 9421 Section 2.1.2), signature-agent coverage when the
header is present, the RFC 7638 keyid check under tag="web-bot-auth",
and skipping only the signature that uses another component parameter.
buildSignatureBase takes a name or a [name, key] pair.
@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-triage Signal that the PR is ready for human triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants