Repository navigation
Description
Context
getInvoice in backend/src/routes/invoice.handlers.ts returns the full stored record, including customer name, email and payer identity, when ?sellerPublicKey= matches the invoice's seller. That same key is part of PublicInvoiceDto (shared/invoice.ts) and is printed on the pay page, so anyone holding a pay link can read the seller workspace. getInvoices, getStats and getPaymentEvents use the same query-string check, and createInvoice accepts any sellerPublicKey with no proof, so pay links can be minted under someone else's wallet. Cancel signs cancel:<id> (signInvoiceCancelMessage in frontend/lib/stellar.ts). That message has no domain, network, nonce or expiry, so a captured signature stays valid forever.
Goal
Seller-only endpoints take the seller from a short-lived session that the wallet earned by signing a server-issued challenge. A public key in a query string or body never grants access by itself. Freighter stays the only identity, with no account table.
Work
- Add
GET /api/auth/challengeandPOST /api/auth/session, built on the SEP-10 helpers already in@stellar/stellar-sdk(WebAuth.buildChallengeTx,readChallengeTx,verifyChallengeTxSigners). Bind them to the pinned network passphrase fromshared/network.tsand a configured home domain. Nonces are single-use and expire within 5 minutes - Issue an HMAC-signed session token carrying the seller key, network and an expiry of at most 1h, plus a key id so the secret can be rotated from env
- Add a
requireSellerSessionmiddleware and movegetInvoices,getStats,getPaymentEvents, the workspace branch ofgetInvoice,createInvoiceandcancelInvoiceonto it. A body or querysellerPublicKeythat differs from the session key returns 403 - Add
AuthorizationtocorsOptions().allowedHeadersinbackend/src/config/runtime.ts, which today allows onlyContent-TypeandAccept - In the frontend, the wallet session gets a token through Freighter
signTransaction, keeps it in memory per account and network, drops it on account or network switch, and challenges again on 401.frontend/lib/api.tsattaches the token andmock-api.tsmirrors the contract - Retire the
cancel:<id>blob path in favour of the session. Any transition fallback sits behind an env flag with a removal note - Add tests for the pay-link-to-workspace leak, replayed or expired challenges, a token from the other network, and a wallet switch mid-dashboard
Expected diff
18–25 files. A new auth route and middleware, the handlers moved onto them, and the frontend wallet session and API client attaching the token.
Areas involved
backend/src/routes/invoice.handlers.ts, backend/src/routes/invoice.routes.ts, backend/src/utils/signature-verification.ts, backend/src/config/runtime.ts, backend/src/config/stellar.ts, backend/src/server-dual.ts, backend/src/server-mvp.ts, backend/src/server.ts, backend/env.example.txt, backend/env.mvp.example, shared/network.ts, shared/invoice.ts, frontend/lib/stellar.ts, frontend/lib/wallet-session.js, frontend/lib/wallet-session.d.ts, frontend/lib/api.ts, frontend/lib/mock-api.ts, frontend/app/dashboard/page.tsx, frontend/app/invoice/[id]/page.tsx, frontend/components/PaymentEventsFeed.tsx, frontend/components/InvoiceForm.tsx, backend/tests/cross-seller-isolation.test.ts, backend/tests/wallet-scoped-invoices.test.ts, backend/tests/invoice-handlers.test.ts, frontend/tests/wallet-session.test.js, backend/src/routes/auth.routes.ts (new), backend/src/middleware/seller-session.ts (new), backend/tests/seller-session.test.ts (new).
Done when
-
GET /api/invoices/:id?sellerPublicKey=<seller key from the public DTO>without a session returns only public fields. A named test reproduces the pay-link path and asserts there is nocustomerEmail - List, stats, events, create and cancel return 401 without a session, and 403 when the body or query key differs from the session key
- A challenge can be redeemed once. Expired, reused or other-network challenges are rejected with a typed code
- Switching the Freighter account on the dashboard drops the old token before the next fetch
- A CORS preflight from the configured frontend origin allows
Authorization
Activity
Rodrigoue9 commentedon Oct 3, 2026
Hi @Quittance-Labs team, I would like to work on this issue!
Architectural Approach
/api/auth/challenge) issuing a cryptographically secure nonce, timestamp, and strict TTL (5 minutes) held in-memory/cache.getInvoice.PublicInvoiceDtoare only disclosed to the proven owner.Proof of Work
Senior full-stack Web3 engineer specialized in Stellar/Soroban architecture (contributor to Astrea-Protocol #251, stellar-did-credit #898, and Documenso #3275).
Could you please assign this issue to me so I can open the PR? Thanks!
Ranjeet2063 commentedon Oct 4, 2026
Hello maintainers,
I am interested in taking up
Prove seller wallet ownership with a signed challenge session instead of a public key in the query string.Plan:
Please assign this to me if the plan looks good. Thanks!
Cover every seller read with one wallet-session signature
woahwhattheheck commentedon Oct 9, 2026
/claim #586
I am the original author of the implementation in PR #589: #589. Please review this existing submission for acceptance and any eligible GrantFox OSS contributor reward under actual program terms; if approved, confirm allocation, payout route and timing. No particular award or settlement is claimed as received.