--- name: implementing-authentication description: Implements identity verification for backend services — password storage, session cookies vs JWT, OAuth2/OIDC login flows, MFA, and password reset. Use when the user asks to add login, signup, authentication, sessions, JWTs, OAuth/OIDC or social login, password hashing, or password reset to a service. Do not use for permission checks after login (implementing-authorization) or for database credentials and roles (securing-databases). --- # Implementing Authentication ## When to use / when NOT to use - **Use for:** login/signup flows, password storage, session or token management, OAuth2/OIDC integration, MFA, password reset. - **Do NOT use for:** deciding what a logged-in user may do (implementing-authorization), database users/roles (db-skills/securing-databases), or general service hardening (securing-backend-services). ## Core rules 1. **Hash passwords with argon2id.** bcrypt (cost ≥12) is the acceptable fallback when argon2 is unavailable. - ✅ `argon2id(password)` via a maintained library - ❌ `sha256(password + salt)` — fast hashes are crackable at scale; never MD5/SHA-family alone 2. **First-party web apps default to server-side sessions in cookies**, not JWTs. - Cookie flags always: `HttpOnly; Secure; SameSite=Lax` (Strict for admin surfaces). - JWTs are for service-to-service and mobile/SPA APIs: expiry ≤15 min, paired with rotating refresh tokens, revocation list for logout. 3. **Third-party login uses OAuth2 authorization code + PKCE.** Never the implicit flow; never roll your own OAuth client if the framework has one. 4. **Rate-limit credential endpoints** (login, signup, reset): per-IP and per-account. Check new passwords against a breach corpus (e.g. haveibeenpwned k-anonymity API). 5. **Password reset:** single-use token, expiry ≤1 hour, stored hashed, sent by email link only. - ✅ Response is identical whether the account exists or not ("If that address exists, we sent a link.") - ❌ "No account with that email" — user enumeration 6. **MFA hooks:** TOTP as default second factor; enforce at login and before sensitive actions (payout, email change). Recovery codes generated once, stored hashed. 7. **Never log or echo credentials, tokens, or session IDs.** Rotate the session ID on privilege change (login, MFA pass) to block session fixation. ## Workflow 1. Pick the mechanism with the decision rule in rule 2 (sessions vs JWT vs OIDC). 2. Implement storage: user table with `password_hash` (argon2id), no plaintext or reversible encryption anywhere. 3. Implement the flow with the framework's primitives (e.g. FastAPI + `authlib`, Express + `passport`); wire rate limits (rule 4). 4. Add reset + MFA per rules 5–6. 5. **Validate:** attempt login with wrong password (must fail generically), replay an expired/rotated token (must fail), inspect the Set-Cookie header for `HttpOnly; Secure; SameSite`, and confirm the reset flow gives identical responses for existing and unknown emails. Fix and re-test until all four pass. ## Edge cases & failure modes - **Existing weak hashes (MD5/SHA1)** → rehash transparently on next successful login; force reset for dormant accounts. - **Clock skew with JWTs** → allow ≤60 s leeway on `exp`/`nbf`, never more. - **Lockout abuse** (attacker locking victims out) → prefer progressive delays + CAPTCHA over hard lockout. - **OAuth provider returns unverified email** → treat as unverified; require confirmation before linking accounts. ## References Copy-paste patterns and gotchas: see [references/patterns.md](references/patterns.md).