design·lab

Security · applied

Login, Sessions & Auth

HTTP forgets you after every request, so login has to manufacture memory — safely, without ever touching a plaintext password.

✗ Problem

Stateless HTTP + plaintext passwords = disaster

Every request arrives with no memory of the last one — so how does the server know request #2 is still "alice"? And if the password table is stored as-is, one breach hands over every account.

// ✗ never do this
const users = {
  alice: { password: "hunter2" }  // plaintext!
};
// DB leak → every password exposed, instantly,
// including any site where a user reused it.
HTTP has no built-in identity across requests, and reversible password storage turns a single breach into total account takeover — for every user, everywhere they reused that password.
✓ How it works

Verify a salted hash, then hand back a credential

const ok = await bcrypt.compare(pw, user.hash);
if (!ok) throw Error("401");

// stateful: server stores the session
session.id = randomId();
store.save(session.id, user.id);

// or stateless: self-contained token
const token = jwt.sign(
  { sub: user.id }, secret, { expiresIn: "15m" }
);
Login
user + pw
Verify hash
bcrypt.compare
Issue token
session / JWT
Authed requests
cookie / Bearer

Session cookie: server keeps the state — trivial to revoke, needs a lookup per request. JWT: stateless and self-contained — scales without a store, but revoking one token before it expires is hard. Either way: short expiries, refresh tokens to renew silently, and a real logout path that deletes the session or blacklists the token.

✓ See it live

Log in, call the API, watch the session expire

Fixed user alice / secret123. Time is a logical clock (ticks), not the wall clock — so the demo is deterministic every run.

🔒 Logged out

Try the wrong password first, then the correct one.

— log empty —
✓ Takeaway

Hash it, expire it, and be ready to revoke it

  • Never store plaintext. Salt + hash with bcrypt or argon2 — compare with a constant-time check.
  • Sessions vs JWT is a tradeoff: easy revocation + server state, vs. statelessness + harder revocation.
  • Short expiries + refresh tokens limit the damage window of a stolen credential.
  • HttpOnly + Secure cookies keep tokens out of reach of JavaScript and off plain HTTP.
  • Add MFA for a second factor, and rate-limit the login endpoint against credential stuffing.
🎯 Relates: delegated login is OAuth2/OIDC; broken auth is a top OWASP risk; protect the login endpoint with a rate limiter.