Background
All merchant-facing endpoints (dashboard, invoices, profile, API keys) must require a valid JWT. This middleware needs to be reusable, attach the resolved merchant to req, and return clear errors for missing, malformed, or expired tokens.
Proposed Steps
- Create
src/middleware/auth.middleware.ts
- Extract the Bearer token from the
Authorization header
- Verify and decode the JWT using
JWT_SECRET
- Load the
Merchant record from the database using sub from the token
- Attach it to
req.merchant (extend Request type in src/types/express.d.ts)
- Call
next() on success; return 401 on any failure
- Apply the middleware to all
/api/v1/merchants/*, /api/v1/invoices/*, and future protected route groups
Acceptance Criteria
- Request with valid JWT →
req.merchant is populated and next() is called
- Missing
Authorization header → 401 { error: "Authentication required" }
- Malformed or expired JWT →
401 { error: "Invalid or expired token" }
- Valid JWT but merchant deleted from DB →
401
- Middleware is applied at the router level (not per-route) for all protected groups
- TypeScript types are extended so
req.merchant is typed as Merchant throughout the codebase
Background
All merchant-facing endpoints (dashboard, invoices, profile, API keys) must require a valid JWT. This middleware needs to be reusable, attach the resolved merchant to
req, and return clear errors for missing, malformed, or expired tokens.Proposed Steps
src/middleware/auth.middleware.tsAuthorizationheaderJWT_SECRETMerchantrecord from the database usingsubfrom the tokenreq.merchant(extendRequesttype insrc/types/express.d.ts)next()on success; return401on any failure/api/v1/merchants/*,/api/v1/invoices/*, and future protected route groupsAcceptance Criteria
req.merchantis populated andnext()is calledAuthorizationheader →401 { error: "Authentication required" }401 { error: "Invalid or expired token" }401req.merchantis typed asMerchantthroughout the codebase