Lesson 5 / 25
Designing the Login Flow
Generic errors, throttling and lockout trade-offs.
What a careful login endpoint does
Return the same generic message ("Invalid email or password") and similar timing whether the account exists or not, which limits enumeration; when the user is unknown, still run a hash verification against a dummy hash. Rate limit by account and by source (IP, device fingerprint) because stuffing spreads across IPs while spraying spreads across accounts. Hard lockout after N failures lets attackers lock out victims (a denial of service), so many systems prefer progressive delays, CAPTCHAs or step-up challenges, and notify the user. Follow NIST SP 800-63B guidance: favour length over composition rules, allow paste and password managers, check new passwords against breached-password lists, and do not force periodic rotation without evidence of compromise.
A login handler
Express-style TypeScript; rateLimiter, verifyPassword and audit are your own helpers.
const DUMMY_HASH = await hashPassword(crypto.randomUUID()); // computed once at startup
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const key = `login:${email.toLowerCase()}`;
if (!(await rateLimiter.allow(key)) || !(await rateLimiter.allow(`ip:${req.ip}`))) {
return res.status(429).json({ error: 'Too many attempts. Try again later.' });
}
const user = await db.users.findByEmail(email);
// verify against a dummy hash when the user is unknown to keep timing similar
const ok = await verifyPassword(user?.passwordHash ?? DUMMY_HASH, password);
if (!user || !ok) {
audit('login_failed', { email, ip: req.ip });
return res.status(401).json({ error: 'Invalid email or password' });
}
await rateLimiter.reset(key);
await startSession(req, res, user); // regenerates the session id
audit('login_succeeded', { userId: user.id, ip: req.ip });
res.json({ ok: true });
});Registration and reset leak too
Enumeration is not just a login problem. "This email is already registered" on sign-up, or a different reset message for unknown addresses, gives the same information away. Use neutral wording and send details by email instead.
Quick check: What is a downside of hard account lockout after a few failed attempts?
- It makes passwords easier to guess
- Attackers can deliberately lock out legitimate users
- It reveals the password hash
- It disables HTTPS for that user
Answer
Attackers can deliberately lock out legitimate users — Lockout turns into a denial-of-service tool; progressive delays and step-up checks are gentler.