Skip to content

feat(auth): bind request payloads into auth proofs#172

Merged
sirdeggen merged 3 commits into
bsv-blockchain:mainfrom
kjartan221:@bsv/auth-update
Jun 10, 2026
Merged

feat(auth): bind request payloads into auth proofs#172
sirdeggen merged 3 commits into
bsv-blockchain:mainfrom
kjartan221:@bsv/auth-update

Conversation

@kjartan221

Copy link
Copy Markdown
Collaborator

Summary

@bsv/auth is documented as authenticating requests, but createAuthProof /
verifyAuthProof only ever signed { action, identityKey, expiresAt, nonce }. There
was no way to bind a request's payload, so for any action that carries a body (e.g. a
username change → { newUsername }) the body was not covered by the signature — an
intermediary could alter it and the proof still verified. This PR closes that gap by
letting a proof optionally bind the request body, and tidies the now-too-long parameter
list into a named-args object.

What changed

  • Optional request-body binding. createAuthProof and verifyAuthProof accept an
    optional body. The client signs over the auth fields plus the body bytes; the
    verifier binds the raw received body the same way. A tampered, missing, or injected
    body fails the signature check. The body is bound only — it is not added to
    proof.data and is transmitted with the request as usual.
  • Canonical encoding. When a body is present it is appended length-prefixed
    (VarInt length + bytes via Utils.Writer), so arbitrary binary is unambiguous. With
    no body the signed bytes are exactly serializeAuthSigData(data) as before.
  • Body normalization (normalizeBody): string → UTF-8, ArrayBuffer / typed
    array → raw bytes, plain object or any array → JSON. Binary must be passed as a typed
    array / ArrayBuffer; arrays are always treated as structured data.
  • Named-args API. Both functions now take a single object (CreateAuthProofArgs /
    VerifyAuthProofArgs, both extending AuthProofOptions), matching the @bsv/sdk
    wallet-method style and removing the fragile 5-positional-arg ordering. The
    AuthProofClient / AuthProofServer wrappers take the same object minus the bound
    options.
  • Perf benchmark. Opt-in npm run test:perf (dedicated jest.perf.config.js,
    excluded from the default run) measuring create/verify vs. the pure serialization
    functions.

Why

The lightweight signed-proof scheme is meant to authenticate any single request, not
just login. Authenticating the action but not the body is a real integrity gap for
any write request. Length-prefixed binding is the minimal, transport-agnostic way to
cover the payload (same approach as AuthFetch's request serialization) without
changing the proof's shape or its single-use / expiry semantics.

Why this fixes the issue

A body-bearing request signed via createAuthProof({ …, body }) and verified via
verifyAuthProof({ …, body }) now fails verification if the body differs by a single
byte — so the proof genuinely authenticates the request contents the docs promised, not
just the action. Login and other bodyless calls are unaffected.

Backward compatibility

  • Omitting body produces byte-identical signed bytes to the previous version, so
    existing bodyless (login) proofs verify unchanged.
  • The call signature changed from positional to a single args object
    (expectedActionaction); this is the one breaking change. Downstream callers
    were updated in tandem.

Testing

  • npm test — full unit + real-ProtoWallet round-trip suite (same-body verifies;
    tampered / missing / injected / binary bodies behave correctly; login unchanged).
  • npm run test:perf — confirms body binding adds ~12 µs over a ~5 ms signing op
    (≈0.2%), i.e. no measurable overhead.

@kjartan221 kjartan221 requested a review from sirdeggen as a code owner May 29, 2026 22:49
- jest.config.js: use String.raw to avoid escaping backslashes in regex
- authProof.perf.test.ts: import from 'node:perf_hooks' for consistency

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@sirdeggen sirdeggen merged commit 90e98e1 into bsv-blockchain:main Jun 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants