Description
All POST, PUT, PATCH, and DELETE API routes currently accept requests without verifying that they originate from the app's own frontend, making them vulnerable to cross-site request forgery. Adding CSRF token validation closes this attack vector without breaking the existing wallet-based authentication flow.
Technical Context
A CSRF token utility at src/lib/csrf.ts generates and validates tokens using the crypto Web API (crypto.randomUUID + HMAC-SHA256 with a CSRF_SECRET environment variable). A server-side helper src/lib/middleware/csrfMiddleware.ts validates the X-CSRF-Token header on all mutating routes (e.g., src/app/api/invoices/route.ts, src/app/api/recipients/route.ts). A client-side hook src/hooks/useCsrfToken.ts fetches the token from GET /api/csrf and injects it into all fetch calls via a centralized src/lib/apiClient.ts wrapper.
Acceptance Criteria
Description
All
POST,PUT,PATCH, andDELETEAPI routes currently accept requests without verifying that they originate from the app's own frontend, making them vulnerable to cross-site request forgery. Adding CSRF token validation closes this attack vector without breaking the existing wallet-based authentication flow.Technical Context
A CSRF token utility at
src/lib/csrf.tsgenerates and validates tokens using thecryptoWeb API (crypto.randomUUID+ HMAC-SHA256 with aCSRF_SECRETenvironment variable). A server-side helpersrc/lib/middleware/csrfMiddleware.tsvalidates theX-CSRF-Tokenheader on all mutating routes (e.g.,src/app/api/invoices/route.ts,src/app/api/recipients/route.ts). A client-side hooksrc/hooks/useCsrfToken.tsfetches the token fromGET /api/csrfand injects it into allfetchcalls via a centralizedsrc/lib/apiClient.tswrapper.Acceptance Criteria
403 Forbiddenwhen theX-CSRF-Tokenheader is absent or invalidGET /api/csrfreturns a fresh signed token that is valid for exactly 60 minutesuseCsrfTokenhook automatically refreshes the token before expiry and retries the queued request transparentlyprocess.env.CSRF_SECRET; the app fails to start at build time if the variable is missingGETroutes are unaffected and require no token