Skip to content

key in SignatureParams is silently ignored on v0.1.3, producing the superseded Signature-Agent form #126

Description

@sidhant2402

main already builds the dictionary Signature-Agent form correctly — this is about the published release, and specifically about it failing quietly rather than loudly.

On web-bot-auth@0.1.3 (current latest, published 2026-03-09), key in SignatureParams is accepted, type-checked, and has no effect on the signature base. A caller asking for the dictionary binding gets the superseded form back with no error and no warning.

Environment

  • web-bot-auth@0.1.3 (npm latest; the version list ends at 0.1.3)
  • Node 24.19.0

Reproduction

A signer that records the base instead of signing it:

const { signatureHeaders } = require("web-bot-auth");

const KEYID = "poqkLGiymh_W0uP6PZFw-dvez3QJT5SolqXBCW38r0U";
const NONCE =
  "n9p433xm+NJ3ph3upfBIGmsuwHw387YV7Q/F+6BSpGCVjYCqQw6rznNA8PVVLySrAWsv0hQtFioQb6E1YsauiA==";

const out = {};
const signer = {
  alg: "ed25519",
  keyid: KEYID,
  async sign(data) { out.base = data; return new Uint8Array(64); },
};

await signatureHeaders(
  {
    url: "https://example.com/path/to/resource",
    method: "GET",
    headers: { "signature-agent": 'agent2="https://signature-agent.test"' },
  },
  signer,
  {
    created: new Date(1735689600000),
    expires: new Date(4889289600000),
    nonce: NONCE,
    key: "agent2",                                 // asks for the dictionary binding
    components: ["@authority", "signature-agent"],
  },
);

console.log(out.base);

Expected

Per draft-meunier-webbotauth-httpsig-protocol-01 §5.2.1 and Appendix E.2.2, and matching packages/web-bot-auth/test/test_data/web_bot_auth_architecture_v2.json[3] at 57fc787:

"@authority": example.com
"signature-agent";key="agent2": "https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent";key="agent2");created=1735689600;…

Actual

"@authority": example.com
"signature-agent": agent2="https://signature-agent.test"
"@signature-params": ("@authority" "signature-agent");created=1735689600;…

The component parameter is absent from both the covered-component line and @signature-params; the raw header value is serialised instead. key made no difference — the same base is produced with key omitted entirely.

Passing the object component form that main's recommendedComponents() returns is rejected on 0.1.3:

components: ["@authority", { header: "signature-agent", key: "agent2" }]
// throws: signature-agent is required in params.components when included as a header param

and recommendedComponents is not part of the 0.1.3 export surface. So on the published release the dictionary form is not reachable by any calling convention I could find.

Cause

#63 ("Support draft-directory-04 with sf-dictionary signature-agent") was merged 2026-05-26. v0.1.3 was published 2026-03-09, about two and a half months earlier. The gap is release timing, not implementation — I read packages/web-bot-auth/src/index.ts at 57fc787 and recommendedComponents() returns the object component form as expected.

Scope

Narrow, and worth stating so this isn't read as broader than it is. Comparing bases against an independent implementation, 0.1.3 agrees exactly on:

  • E.2.1 (@authority only)
  • E.2.3 (the legacy sf-string Signature-Agent form)
  • @method, @authority, @path
  • all signature parameters, their values, and their ordering

The only divergence is the dictionary Signature-Agent form.

Suggestion

A release carrying #63 would resolve it. If one isn't imminent, it may be worth having 0.1.x reject key rather than ignore it — throwing "dictionary Signature-Agent requires vX" would turn a silent wrong base into an obvious upgrade prompt. As it stands, a signer built on latest emits the superseded form while its author believes it asked for the dictionary form, and the failure only surfaces at a verifier that enforces the draft.

Not a security report

This is a wire-format difference. No signature is forgeable because of it, nothing is bypassed, and I'm making no claim about the behaviour of any deployed Cloudflare verifier — this is the npm library only. Filing it as an interoperability and ergonomics issue.

Happy to open a PR for the key-rejection guard if that would be useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions