feat(auth): bind signed request signatures to audience (closes #1462) - #1901
Merged
kryputh merged 2 commits intoJul 29, 2026
Merged
Conversation
…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>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
audiencefield from the issue's proposed payload(
version, method, route, body hash, nonce, issued-at, audience) and backsevery binding property with an end-to-end cryptographic test, not just
string comparison.
Changes
src/server/api/auth/signed-request.ts: addx-stealth-audiencetoSIGNED_REQUEST_HEADERSso it's folded into the canonical string like theother signed headers, and add
validateSignedRequestAudience()— abounded active-audience-set check (mirrors
validateAuthVersion) so asignature 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 freshEd25519 keypair,
x-stealth-audienceon every vector, and a newaudience_mismatchvector (signature is cryptographically valid, butscoped to the wrong deployment).
tests/unit/api/auth/signed-request-binding.test.ts(new): signs onebase 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 thenew audience vector and asserts
validateSignedRequestAudienceagainstthe 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)
Out of scope
This is the canonicalization primitive only.
src/server/api/context.tsstill authenticates purely off the
x-stealth-addressheader — actualsignature verification is not yet wired into live route handlers. That gap
is already tracked separately (see the
it.failsregressions intests/unit/api/security.regression.test.ts, issue #1555).Test plan
npx tsc --noEmit— cleannpx eslinton changed files — cleannpx vitest run— 141 files / 1677 passed (+ 3 pre-existing,unrelated
it.failsmarkers for Add API security regression tests for authentication and authorization bypasses #1555)Closes #1462