After this lesson, you will be able to: Hash passwords with bcrypt or Argon2id, secure sessions with the right cookie flags, avoid the common JWT pitfalls, implement TOTP-based MFA, and harden an OAuth flow.
Authentication is where most apps get breached. This lesson covers password hashing (never plaintext; Argon2id or bcrypt), session management with HttpOnly/Secure/SameSite cookies, JWT security including the alg:none attack, MFA with TOTP, and OAuth security (state parameter, redirect URI validation).
Never store plaintext passwords, and never use a fast hash like SHA-256 for them (attackers compute billions per second on a GPU). Use a slow, salted password hash. Argon2id is the current recommendation (memory-hard, resists GPU and ASIC attacks); bcrypt is still acceptable and widely supported. Both salt automatically. The 'cost' parameter (bcrypt rounds, Argon2 memory/iterations) is tuned so a single hash takes a fraction of a second on your server, making mass cracking infeasible. Re-calibrate cost as hardware gets faster.
Argon2id with sensible defaults. The salt is generated and stored inside the hash string automatically.
import argon2 from "argon2";// On signup / password changeconst hash = await argon2.hash(plainPassword, { type: argon2.argon2id });// store `hash` in the DB; never store the plain password// On loginconst ok = await argon2.verify(storedHash, submittedPassword);if (!ok) {// generic error: do not reveal whether the email or the password was wrongreturn { error: "Invalid email or password" };}
Session cookies must be HttpOnly (JavaScript cannot read them, blocking XSS theft), Secure (sent only over HTTPS), and SameSite (Strict or Lax to blunt CSRF; None requires Secure and is only for genuine cross-site needs). On logout, invalidate the session server-side (delete the session record), not just the cookie, otherwise a stolen cookie still works. Rotate the session identifier on login to prevent session fixation, where an attacker plants a known session ID before the victim authenticates.
A JSON Web Token is signed data the server can verify without a database lookup. The classic vulnerability is alg:none: an attacker changes the header's algorithm to 'none' and removes the signature; a naive library accepts it. Always verify the signature with a pinned algorithm and reject 'none'. Keep access tokens short-lived and use refresh-token rotation. Do not store anything sensitive in localStorage (any XSS reads it); for sessions prefer HttpOnly cookies over localStorage-held JWTs.
TOTP (the codes in Google Authenticator/Authy) works from a shared secret and the current time, hashed into a 6-digit code valid for a 30-second window. The server stores the shared secret once at enrollment and verifies codes against it. SMS is a weak second factor (SIM swapping, interception); prefer TOTP or hardware keys (FIDO2/WebAuthn). Always issue recovery codes and store them hashed. For OAuth, use the state parameter to prevent CSRF on the callback, strictly validate the redirect URI against an allowlist, and avoid the deprecated implicit flow in favor of authorization code with PKCE.
Pick the best reason.
Using SHA-256 or MD5 for passwords instead of a slow salted hash. Revealing whether the email or the password was wrong (lets attackers enumerate accounts; return one generic error). Storing JWTs in localStorage where XSS can steal them. Accepting alg:none or letting the client pick the algorithm. Only deleting the cookie on logout while the server-side session stays valid. Treating SMS as strong MFA. Skipping the OAuth state parameter, leaving the callback open to CSRF.
Sign in and purchase access to unlock this lesson.