Skip to content

feat(auth): bind signed request signatures to audience (closes #1462) - #1901

Merged
kryputh merged 2 commits into
Stellar-Mail:mainfrom
Nanle-code:feat/auth-signed-request-audience-1462
Jul 29, 2026
Merged

feat(auth): bind signed request signatures to audience (closes #1462)#1901
kryputh merged 2 commits into
Stellar-Mail:mainfrom
Nanle-code:feat/auth-signed-request-audience-1462

Conversation

@Nanle-code

Copy link
Copy Markdown
Contributor

Summary

Binds the v1 signed-request canonical payload to the deployment audience,
closing the last gap identified in #1462. The canonicalizer already scoped
signatures to version, HTTP method, canonical route, and body digest — this
adds the missing audience field from the issue's proposed payload
(version, method, route, body hash, nonce, issued-at, audience) and backs
every binding property with an end-to-end cryptographic test, not just
string comparison.

Changes

  • src/server/api/auth/signed-request.ts: add x-stealth-audience to
    SIGNED_REQUEST_HEADERS so it's folded into the canonical string like the
    other signed headers, and add validateSignedRequestAudience() — a
    bounded active-audience-set check (mirrors validateAuthVersion) so a
    signature scoped to one deployment (e.g. staging) can't be replayed
    against another that trusts the same key.
  • test-fixtures/auth/signed-request-v1.json: regenerated with a fresh
    Ed25519 keypair, x-stealth-audience on every vector, and a new
    audience_mismatch vector (signature is cryptographically valid, but
    scoped to the wrong deployment).
  • tests/unit/api/auth/signed-request-binding.test.ts (new): signs one
    base request with a real Ed25519 key, then proves verification actually
    fails once method, route, query, body, or audience changes — and still
    succeeds for equivalent re-encodings (reordered query params, header
    casing/whitespace differences).
  • tests/unit/api/auth/signed-request-vectors.test.ts: exercises the
    new audience vector and asserts validateSignedRequestAudience against
    the fixture's accepted audience.
  • docs/security/api-authentication-v1.md: documents the new header,
    canonical-string line, verification-order step, and error table entry.

Acceptance criteria (#1462)

  • A signature for one method cannot authorize another method
  • A signature for one route cannot authorize another route
  • Changing the body invalidates the signature
  • Canonicalization fixtures are deterministic across equivalent requests

Out of scope

This is the canonicalization primitive only. src/server/api/context.ts
still authenticates purely off the x-stealth-address header — actual
signature verification is not yet wired into live route handlers. That gap
is already tracked separately (see the it.fails regressions in
tests/unit/api/security.regression.test.ts, issue #1555).

Test plan

Closes #1462

Nanle-code and others added 2 commits July 29, 2026 08:41
…ayload gap (Stellar-Mail#1462)

Add x-stealth-audience to the v1 canonical signing payload alongside the
already-bound method, route, and body digest, plus validateSignedRequestAudience
to reject signatures scoped to a different deployment. Regenerate the
interoperability fixture with a fresh keypair and an audience-mismatch vector,
and add signed-request-binding.test.ts, which signs a real request and proves
end-to-end that changing method/route/query/body/audience invalidates the
signature while equivalent re-encodings still verify.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kryputh
kryputh merged commit b0eaeac into Stellar-Mail:main Jul 29, 2026
5 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.

Bind authenticated signatures to HTTP method, route, and body digest

2 participants