Warning: Pure account lockout creates DoS vector. Combine with IP throttling, CAPTCHA, and risk-based unlocks.
2. Setting Failed Login Threshold
Account Type
Threshold
Window
Standard user
5–10 attempts
15 min
Admin / privileged
3–5 attempts
15 min
Service account
3 attempts
5 min
High-risk (banking)
3 attempts
1 hour
3. Implementing Automatic Lockout
Example: Redis-backed counter
const MAX = 5, WINDOW = 900, LOCK = 1800;async function recordFailure(userId) { const key = `auth:fail:${userId}`; const count = await redis.incr(key); if (count === 1) await redis.expire(key, WINDOW); if (count >= MAX) { await redis.set(`auth:lock:${userId}`, "1", "EX", LOCK); await notifier.lockoutEmail(userId); }}async function isLocked(userId) { return Boolean(await redis.get(`auth:lock:${userId}`));}
4. Setting Lockout Duration
Strategy
Pattern
Fixed
15 or 30 minutes
Exponential backoff
5, 15, 60, 240 min per re-trigger
Permanent
Requires admin / email unlock
Risk-based
Length tied to risk score
5. Implementing Manual Account Unlock
Mechanism
Auth Required
Admin console
Admin role + audit log
Self-service via email link
Email ownership
Self-service via MFA
Valid second factor
Support ticket
ID verification
6. Using Email Notification for Lockout
Example: Lockout email payload
Subject: Your account was locked due to suspicious activityHi {name},We locked your account after 5 failed login attempts from: IP: 198.51.100.42 (United States) Time: 2026-05-18 14:32 UTC Device: Chrome 124 on macOSIf this was you, click here to unlock: https://app/unlock?t=XYZIf not, change your password immediately.
7. Implementing Account Unlock Flow
Self-Service Unlock
User receives unlock email with signed token
Click link → verify token signature + expiry (1 hour)
Optional: require MFA challenge
Clear failure counter and lockout key
Force password reset if lockout caused by suspected breach
Audit log unlock event
8. Preventing Account Enumeration via Lockout
Anti-pattern
Fix
"Account locked" response
Return same generic error as bad password
Different timing for locked vs unknown
Constant-time path
Only locking real accounts
Lockout signals "account exists"
Solution
Track failures by IP+email combo, not just username