feat(api): add HMAC-SHA256 webhook signature validation for incoming Stellar webhooks - #4
Open
vickydve wants to merge 9 commits into
Open
feat(api): add HMAC-SHA256 webhook signature validation for incoming Stellar webhooks#4vickydve wants to merge 9 commits into
vickydve wants to merge 9 commits into
Conversation
…Stellar webhooks Add POST /api/v1/webhooks/stellar to receive Stellar account/ transaction event notifications. Requests must carry an X-Stellar-Webhook-Signature header with the hex-encoded HMAC-SHA256 digest of the raw request body, keyed with STELLAR_WEBHOOK_SECRET; the signature is verified with a constant-time comparison before the payload is parsed against its Zod schema, and the server fails closed (503) when no secret is configured rather than accepting unsigned requests. express.json() now captures the raw request bytes via its `verify` callback so the signature can be computed over exactly what the client signed, not a re-serialized copy of the parsed body. Also restores backend/src/contracts/transactions.ts, which a prior cleanup commit deleted as unused dead code without removing its only caller (api/routes/transactions.ts) — that left main unable to compile or run its test suite at all. Closes BigNathan1#170
8 tasks
Formalizes BigNathan1 as sole maintainer with write access to main, and replaces the old maintainer-nomination path with the CONTRIBUTORS.md leaderboard for recognition.
Closes BigNathan1#153. Adds full Stellar account details endpoint (sequence, thresholds, flags, balances, signers, data) with Zod validation, exponential backoff retries on Horizon 429/503, mapped error responses, unit + testnet integration tests, and OpenAPI/CHANGELOG updates.
Closes BigNathan1#155. Adds POST /api/v1/trustlines/build for unsigned changeTrust XDR (Freighter/Albedo client-side signing), with optional custom limits, Zod validation, mapped Horizon errors, tests, and OpenAPI/CHANGELOG updates. Rebased onto main by the maintainer to resolve a trivial add/add conflict on transactions.ts against BigNathan1#586 — re-verified with tsc --noEmit and the full trustlines/accounts test suite before merge.
…elpers Closes BigNathan1#172. Adds isTestnet()/isMainnet() boolean helpers to StellarService, with unit tests and CHANGELOG update. Rebased onto main by the maintainer to resolve a CHANGELOG.md conflict; re-verified with tsc --noEmit and the stellar.test.ts suite before merge.
Closes BigNathan1#137. Adds GET /api/v1/prices/xlm with CoinGecko/Binance/Coinbase failover, 3.5s per-provider timeout, 30s Redis caching, Zod validation, and unit/integration tests plus OpenAPI/CHANGELOG updates. Rebased onto main by the maintainer to resolve conflicts against BigNathan1#586/BigNathan1#587/BigNathan1#588 (all merged just ahead of this one) in CHANGELOG.md, routes/index.ts, and openapi.yaml; re-verified with tsc --noEmit and the full price/account/trustline test suites before merge.
Closes BigNathan1#166. Extends the Horizon error mapper with the full set of documented transaction and operation result codes, with comprehensive unit test coverage. Rebased onto main by the maintainer to resolve a CHANGELOG.md conflict; re-verified with tsc --noEmit and the horizonError test suite (63 passing) before merge.
…der concurrent requests Closes BigNathan1#169. Adds a per-account, in-memory sequence cache that serializes concurrent access so back-to-back or concurrent submissions from the same source account each get a distinct, correctly-incremented sequence number instead of racing on a stale one. Wired into asset issuance, burn, trustline establishment, and the airdrop payment loop; invalidates and retries once on tx_bad_seq. Rebased onto main by the maintainer to resolve a CHANGELOG.md conflict; re-verified with tsc --noEmit and the sequenceCache/trustlines/stellar test suites before merge.
# Conflicts: # CHANGELOG.md # backend/src/api/routes/index.ts # backend/src/contracts/transactions.ts
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.
What does this PR do?
Adds
POST /api/v1/webhooks/stellarto receive Stellar account/transaction event notifications, protected by HMAC-SHA256 signature verification:express.json()now captures the raw request bytes (req.rawBody) via itsverifycallback inapp.ts, so signature verification runs over exactly what the client signed, not a re-serialized copy of the parsed body.verifyWebhookSignaturemiddleware (api/middleware/webhookSignature.ts) checks theX-Stellar-Webhook-Signatureheader against an HMAC-SHA256 digest of the raw body, keyed withSTELLAR_WEBHOOK_SECRET, using a constant-time comparison. Fails closed with503if no secret is configured,401if the header is missing or doesn't match — signature is checked before the payload is validated.stellarWebhookSchema(Zod) validates the event payload shape after the signature passes.docs/openapi.yamlentry under a newWebhookstag, andSTELLAR_WEBHOOK_SECRETdocumented in.env.example.Addresses BigNathan1/CoopLumen#170 (backlog reference BigNathan1#139).
Unrelated fix bundled in (see commit message)
While wiring an HTTP-level test for the new route,
npx tsc --noEmitturned up thatmaincurrently fails to compile:api/routes/transactions.tsimportsbuildUnsignedPaymentfromcontracts/transactions.ts, which a prior cleanup commit (a11fe5f) deleted as "unused dead code" without removing the only caller. This breaks every test that importsapp— i.e. the whole backend test suite — regardless of this PR. Restored the minimalbuildUnsignedPaymentimplementation somain's ownPOST /api/v1/transactions/unsignedendpoint (and this PR's tests) work again.Type of change
contracts/transactions.ts)Testing
backend/src/api/middleware/__tests__/webhookSignature.test.ts: valid signature passes, missing/invalid/wrong-secret/tampered-payload signatures rejected with 401, unconfigured secret fails closed with 503, missing raw body returns 500. 8 tests.backend/src/api/routes/__tests__/webhooks.test.ts: full HTTP-level coverage via supertest — valid signed payload accepted, missing/invalid signature rejected, malformed-but-signed payload rejected 400 with field errors, signature checked before payload shape, fails closed with no secret configured. 6 tests.npx jest(full suite): 150 passing, 73 skipped (gated on a liveDATABASE_URL), only 1 pre-existing DB-integration failure unrelated to this change (no local Postgres in this environment).npx eslinton all changed files — clean.npx tsc --noEmit— clean (previously failing before thecontracts/transactions.tsrestoration above).Checklist
Generated by Claude Code