Authentication Best Practices
Authentication Best Practices
Section titled “Authentication Best Practices”Introduction
Section titled “Introduction”Authentication is the first line of defense for your application. Getting it wrong can expose user accounts to attackers.
Prerequisite: This page assumes you’ve already set up authentication with Auth.js. If you haven’t, see Phase 5 — Authentication & Authorization first.
Password Security
Section titled “Password Security”Hashing
Section titled “Hashing”import bcrypt from 'bcryptjs'
// ✅ Correct — use bcrypt with sufficient costconst SALT_ROUNDS = 12const hash = await bcrypt.hash(password, SALT_ROUNDS)
// ❌ Wrong — never use MD5, SHA1, or plain textPassword Requirements
Section titled “Password Requirements”- Minimum 8 characters (longer is better)
- No arbitrary complexity rules (they hurt usability more than security)
- Check against common passwords (HaveIBeenPwned API)
- Encourage password managers
Session Security
Section titled “Session Security”| Practice | Why |
|---|---|
| Use HttpOnly cookies | Prevents JavaScript access (XSS protection) |
| Use Secure flag | Ensures HTTPS-only transmission |
| Use SameSite=Strict/Lax | Prevents CSRF attacks |
| Short session expiry | Limits damage if a session is stolen |
| Regenerate session ID after login | Prevents session fixation |
// Setting a secure auth cookiecookies().set('session-token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 60 * 60 * 24, // 24 hours path: '/',})Rate Limiting Logins
Section titled “Rate Limiting Logins”// Prevent brute force attacksconst limiter = new Ratelimit({ redis: Redis.fromEnv(), limiter: Ratelimit.slidingWindow(5, '15 m'), // 5 attempts per 15 min})
const { success } = await limiter.limit(email)
if (!success) { return { error: 'Too many attempts. Try again later.' }}Common Mistakes
Section titled “Common Mistakes”- Returning different errors for “user not found” vs “wrong password” — This lets attackers discover valid emails.
- No rate limiting on login — Without it, attackers can try thousands of passwords.
- Long-lived sessions without refresh — If a session is stolen, the attacker has access for the full duration.
- Storing tokens in localStorage — Vulnerable to XSS. Use HttpOnly cookies.
Summary
Section titled “Summary”Secure authentication uses strong password hashing (bcrypt), secure cookies (HttpOnly, Secure, SameSite), rate limiting on login, and short session expirations. Never store tokens in localStorage and always return generic error messages.