auth · Lesson 11 · Node + Express

The Account Lifecycle

Login gets all the attention, but the flows around it — verify your email, reset a forgotten password, recover a lost device — are where real apps quietly leak. They're "boring," so tutorials skip them and bugs pile up. Good news: they're all the same single-use-token machinery from Lesson 8. The skill today is spotting the handful of footguns that turn a routine reset link into an account-takeover.

🎯 Mission: a mental model of modern auth + the ability to wire it into a real app
The one idea

Email verification, password reset, and recovery are one pattern — a single-use, expiring, hashed token mailed to the user — and their security lives entirely in four footguns. Don't enumerate users, make tokens single-use and short-lived, store only the hash, and invalidate all sessions when the password changes. A password reset is a back door into the account; if it's weaker than your login, attackers walk through it instead.

01 Why these flows are attack magnets

A password reset is, by design, "let someone who lost their credential get a new one." That's also exactly what an attacker wants. So the reset flow is a parallel authentication path — and an attacker will always probe the weakest path, not the front door. Spend your login hardening on a strong password hash and MFA, then leave reset wide open, and none of it mattered.

💡 The reframe: reset IS a login Treat "forgot password" with the same seriousness as the login itself — it grants the same access. Everything you learned about single-use tokens (Lesson 8), hashing (Lesson 4), and session revocation (Lesson 7) converges here. This lesson is mostly applying what you know to a flow people treat as an afterthought. See OWASP Forgot Password Cheat Sheet.

02 The secure password-reset flow

Five steps. Note how much is about what you don't reveal and what you clean up afterward:

① request enter email ② if user exists store token HASH email raw token ③ identical reply (always) ④ click link verify+burn token ⑤ set pw + KILL all sessions Steps ②–③ defeat enumeration; ④ defeats replay/expiry; ⑤ defeats a riding attacker.
The same single-use token as a magic link (Lesson 8) — but the reset adds two reset-specific rules: identical responses (no enumeration) and session invalidation on success.

03 The four footguns (this is the lesson)

User enumerationReturn the identical message ("if an account exists, we sent a link") in ~constant time, whether or not the email is registered. Distinct replies/timings let attackers harvest valid accounts.
Reusable / long-lived linksSingle-use + ≤20 min expiry. Burn the token on use; expire it fast. A reset link sitting valid in an old inbox is a standing key to the account.
Storing the raw tokenStore sha256(token), never the token. Then a database leak can't be turned into account takeovers — the raw token lives only in the email. (Same as Lessons 4 & 8.)
Leaving old sessions aliveInvalidate ALL sessions on reset. If a thief was riding a stolen session, the reset must evict them — otherwise you changed the lock but left them inside (Lesson 7's family revocation).
⚠️ Two more, often missed Rate-limit reset requests per-account (and globally) so attackers can't spray thousands of emails or brute-force tokens. And on a successful reset (or email change), send a notification email to the account — "your password was changed" — so a victim of an unauthorized change finds out immediately. See Authgear — reset best practices.

04 🎯 Your tangible win (5 min): break a reset flow's footguns

A zero-dependency lab runs the secure reset and proves each footgun is closed — identical replies, single-use, expiry, hashed storage, session eviction.

# in your terminal (no npm install needed):
cd ~/projects/learn/public/courses/auth/practice/account-lifecycle-lab
node demo.js
🎯 The win — the footguns closing, verbatim
FOOTGUN 1 — real vs fake email both return:
  "If an account exists for that address, we sent a reset link."   (identical)

RESET: password updated; ALSO invalidated 2 existing sessions
FOOTGUN 2 — same link twice → 🛡️ "token already used"
FOOTGUN 3 — clicked after 21 min → 🛡️ "token expired"
BONUS — DB stores only a hash → a leak can't produce the email's raw token
You watched a reset flow that reveals nothing, can't be replayed, can't be staled, survives a DB leak, and evicts a riding attacker. That's five separate account-takeover paths closed in one flow. Open demo.js — notice requestReset() returns the same string on both branches; that single decision is the whole anti-enumeration defense.

05 Email verification & account recovery

Email verification — the same pattern, gentler stakes

On signup, email a single-use expiring link; clicking it flips email_verified = true. Identical machinery to reset — same four properties — the only difference is what the click does (set a flag, not change a credential). Verifying email matters because so much else (magic links, reset) trusts that the address truly belongs to the user.

Account recovery — the hardest problem in auth

Recovery is "the user lost their factor entirely" (forgot password and lost their phone/passkey). It's genuinely hard because your recovery path is your real security floor — an attacker attacks recovery, not the strong factor. Options, roughly strongest-to-weakest:

Recovery methodStrengthNote
A second registered passkey / deviceStrongEncourage users to enroll two from the start
One-time backup codes (shown at MFA enrollment)StrongUser stores them offline; each single-use
Email reset linkMediumAs strong as the user's inbox
SMS / security questionsWeakSIM-swap & researchable answers — avoid as sole path
💡 Don't let recovery undo your auth If you ship phishing-proof passkeys (Lesson 9) but allow recovery via SMS or security questions, your effective security is SMS/security-questions. Match the recovery strength to the auth strength — the chain is only as strong as its weakest link, and recovery is usually that link.

06 Check yourself

Q1. Your "forgot password" page says "no account with that email" for unknown addresses. Why is that a problem here specifically?

Show answer

It's a user-enumeration oracle (same issue as login in Lesson 4, but reset pages are often forgotten). An attacker submits many addresses and learns which are registered, then targets them for phishing/credential-stuffing. Fix: return an identical message ("if an account exists, we sent a link") in roughly constant time, regardless of existence. (§03 footgun 1.)

Q2. Why must a password reset invalidate all existing sessions, when the user just proved control of their email?

Show answer

Because the reason for the reset might be that an attacker already has a stolen session. Changing the password without killing sessions changes the lock but leaves the intruder inside. Invalidating all sessions (Lesson 7's family revocation) evicts any rider, forcing fresh authentication everywhere. (§03 footgun 4.)

Q3. You deployed passkeys for login but recovery is "answer two security questions." What's the real security level of the account, and why?

Show answer

Roughly the strength of the security questions — i.e. weak. Attackers go after the easiest path, and recovery is a parallel authentication route into the same account. Phishing-proof passkeys don't matter if recovery can be socially engineered. Recovery must match the strength of the primary factor (e.g. a second passkey or offline backup codes). (§05.)

07 Where we go next

You now have every piece a real app needs — login methods, sessions/tokens, MFA, and the lifecycle flows around them. Lesson 12 is the capstone: a decision framework for choosing which of these to combine for a given app, and a complete, running Express auth app that wires the pieces together end to end — signup, hashed passwords, sessions, CSRF, and a protected route you can actually hit. The map becomes a build.

📌 Remember this one line Reset/verify/recovery are single-use-token flows whose security is four footguns: no enumeration, single-use + short expiry, store the hash, and invalidate all sessions on reset. A reset is a back-door login — and recovery is your true security floor, so match its strength to your front door.

💬 Ask me anything — e.g. "wire the reset flow with real email + tokens in Express", "design backup codes for the TOTP lab", or "how do big apps handle account recovery for passkeys?" · Back to Lesson 10 · Lesson 8 · Resources.