# Designing the Login Flow — Authentication & Authorization

Source: https://www.skillbyai.com/en/authentication/p-login

> 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.

```typescript
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.

**Quiz:** What is a downside of hard account lockout after a few failed attempts?

- [ ] It makes passwords easier to guess
- [x] 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.
