How do you handle password hashing and authentication (JWT vs sessions)?
Quick Answer
Never store plaintext passwords — hash with a slow, salted algorithm like bcrypt/argon2. For auth, sessions store state server-side with a cookie session id (easy to revoke), while JWTs are stateless signed tokens the client sends each request (scale well but hard to revoke before expiry). Choose based on revocation and scaling needs.
Detailed Answer
Answer:
Password storage — hash, never encrypt or store plaintext: Use a slow, salted, adaptive hash (bcrypt, scrypt, or argon2) so brute force is expensive:
const bcrypt = require('bcrypt');
// On signup
const hash = await bcrypt.hash(password, 12); // 12 = cost factor
// On login
const ok = await bcrypt.compare(password, user.hash);
Bcrypt salts automatically and lets you raise the cost over time. Never use fast hashes (MD5/SHA-256) for passwords.
Authentication approaches:
Sessions (stateful):
- Server stores session data (in Redis/DB); the client holds only a session id in a cookie.
- Pros: easy to revoke (delete the session), small cookie, server controls state.
- Cons: needs shared session storage when scaling to multiple instances.
JWT (stateless):
- A signed token (header.payload.signature) containing claims; the client sends it (usually
Authorization: Bearer) on each request. The server verifies the signature — no lookup needed. - Pros: stateless → scales horizontally without shared session store; works well across services.
- Cons: hard to revoke before expiry; token bloat; you must protect the secret and keep expiry short.
const jwt = require('jsonwebtoken');
const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET, { expiresIn: '15m' });
const payload = jwt.verify(token, process.env.JWT_SECRET);
Common production pattern: short-lived access JWT + longer-lived refresh token stored server-side (revocable), combining scalability with control.
Also: store tokens safely (httpOnly, Secure cookies mitigate XSS token theft), enforce HTTPS, and rate-limit auth endpoints.