Authentication Patterns
Implement authentication: JWT vs session tokens, refresh token rotation, cookie security, social OAuth flow, and MFA with TOTP.
Authentication is the foundation of application security. Modern auth requires understanding session management, JWT security properties, OAuth 2.0 flows, and multi-factor authentication. Getting auth wrong is costly - prefer battle-tested libraries over custom implementations.
Use an auth library (Lucia, Better Auth, Clerk, Auth.js) for new apps rather than rolling your own. If implementing from scratch: store refresh tokens in httpOnly cookies, use short-lived access tokens, implement token rotation, and never store passwords as plaintext.
Workflow
- Implement JWT access + refresh token patternShort-lived access tokens limit damage from token theft; refresh tokens enable persistent sessions without long-lived JWTs.
- Secure cookies correctlyCookie security properties prevent XSS and CSRF token theft.
- Implement OAuth 2.0 with PKCEOAuth 2.0 with PKCE (Proof Key for Code Exchange) is the secure flow for public clients (SPAs, mobile apps).
- Add TOTP multi-factor authenticationTOTP (Time-based One-Time Password) is the standard authenticator app MFA - significantly reduces account takeover risk.
- Implement secure password hashingPlain-text or weakly hashed passwords make breaches catastrophic.
Output Format
Access token (JWT, 15m) in body, refresh token (opaque, 30d) in httpOnly cookie, token rotation on refresh, OAuth PKCE with state, Argon2id for passwords, TOTP for MFA.
Examples
Refresh endpoint: `app.post("/auth/refresh", async (req, res) => { const token = req.cookies.refreshToken; if (!token) return res.status(401).json({ error: "No refresh token" }); const stored = await db.refreshTokens.findOne({ userId: req.userId }); if (!stored || !await bcrypt.compare(token, stored.hash)) return res.status(401).json({ error: "Invalid" }); await db.refreshTokens.delete(stored.id); const newRefresh = crypto.randomBytes(32).toString("hex"); await db.refreshTokens.create({ userId, hash: await bcrypt.hash(newRefresh, 10) }); setRefreshCookie(res, newRefresh); res.json({ accessToken: signAccessToken(req.userId) }) })`Argon2 hash: `import argon2 from "argon2"; const hash = await argon2.hash(password); // store hash; const valid = await argon2.verify(storedHash, inputPassword); if (!valid) throw new Error("Invalid credentials")`TOTP verify: `import { authenticator } from "otplib"; const isValid = authenticator.verify({ token: req.body.totpCode, secret: decrypt(user.totpSecret, KEY) }); if (!isValid) return res.status(401).json({ error: "Invalid code" })`Edge Cases & Gotchas
- Token rotation race condition: if client sends two concurrent refresh requests (network retry), both may invalidate the token. Use a short window of token reuse (10s) or a database-level mutex
- Account recovery: always provide a recovery path for MFA (backup codes). Generate 10 single-use backup codes at MFA setup: store hashed, allow any one to bypass TOTP
- JWT secret rotation: rotating the JWT signing secret invalidates all active tokens. Use key IDs (kid) in JWT header and support multiple valid keys during rotation
When to Break These Rules
- For B2B SaaS with enterprise requirements, implement SAML/SSO via a library (passport-saml, @boxyhq/saml-jackson) - rolling custom SAML is complex and error-prone.
Reference Files
OWASP Authentication Guide: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.htmlRFC 7519 JWT: https://www.rfc-editor.org/rfc/rfc7519otplib: https://github.com/yeojz/otplibArgon2: https://github.com/ranisalt/node-argon2Better Auth: https://www.better-auth.com/