Authentication
Authentication
Section titled “Authentication”ecommus uses JWT (JSON Web Tokens) with short-lived access tokens and long-lived refresh tokens stored in httpOnly cookies.
Admin Auth Flow
Section titled “Admin Auth Flow”1. POST /api/admin/auth/login { email, password } → 200 { accessToken: "eyJ...", expiresIn: 900 } → Set-Cookie: refreshToken=...; HttpOnly; Secure; SameSite=Strict
2. Include in subsequent requests: Authorization: Bearer <accessToken> X-Tenant-Id: <tenantId>
3. When access token expires (15 min): POST /api/admin/auth/refresh (sends refreshToken cookie automatically) → 200 { accessToken: "eyJ...", expiresIn: 900 }
4. POST /api/admin/auth/logout → Invalidates refresh token in DB → Clears refreshToken cookieToken Configuration
Section titled “Token Configuration”| Setting | Default | Env Variable |
|---|---|---|
| Access token expiry | 15 minutes | JWT_EXPIRY |
| Refresh token expiry | 30 days | JWT_REFRESH_EXPIRY |
| Algorithm | HS256 | — |
| Secret | — | JWT_SECRET (min 32 chars) |
requireAuth Middleware
Section titled “requireAuth Middleware”All admin routes use requireAuth as a preHandler. It:
- Reads the
Authorization: Bearer <token>header - Verifies the JWT signature with
JWT_SECRET - Checks token expiry
- Sets
req.user = { id, email, tenantId, role } - Returns
401 Unauthorizedif any check fails
// How to protect a custom routeapp.get('/api/admin/my-route', { preHandler: [requireAuth, resolveTenant, requireTenant], handler: async (req, reply) => { const userId = req.user.id; // safe to access after requireAuth // ... }});Customer Auth (Storefront)
Section titled “Customer Auth (Storefront)”Customer auth uses the same JWT mechanism but a separate token family:
POST /api/storefront/auth/register { email, password, firstName, lastName }POST /api/storefront/auth/login { email, password }Customer JWTs have role: 'customer' and are scoped to the tenant.
Security Notes
Section titled “Security Notes”- Passwords are hashed with argon2id (never MD5, bcrypt, or plain SHA)
- Refresh tokens are stored in the database and can be revoked (logout or security event)
- All auth endpoints are rate-limited (10 req/min per IP)
- CORS is configured to only allow
FRONTEND_URLandADMIN_URLorigins JWT_SECRETmust be at least 32 characters — startup will fail otherwise