--- name: auth-security description: "JWT, OAuth 2.0 / OIDC, session management, CSRF, password hashing, and MFA enforcement — Applies to: when generating login / signup / password-reset flows; when generating JWT issuance or verification; when generating OAuth 2.0 / OIDC client or server code; when wiring session cookies, CSRF tokens, MFA" --- # Authentication & Authorization Security JWT, OAuth 2.0 / OIDC, session management, CSRF, password hashing, and MFA enforcement ## ALWAYS - For JWT verification, pin the expected algorithm (`RS256`, `EdDSA`, or `ES256`) and verify `iss`, `aud`, `exp`, `nbf`, and `iat`. Reject `alg=none` and any unexpected algorithm. - For OAuth 2.0 public clients (SPA / mobile / CLI), use the **authorization code flow with PKCE** (S256). Never the implicit flow. Never the resource owner password credentials grant. - Cookies for sessions: `Secure; HttpOnly; SameSite=Lax` (or `Strict` for sensitive flows). Use the `__Host-` prefix when there's no subdomain sharing. - Rotate the session identifier on login and on privilege change. Bind the session to the user agent only as a soft signal — never as the sole check. - Hash passwords with argon2id (m=64 MiB, t=3, p=1) and a per-user random salt. Bcrypt cost ≥ 12 or scrypt N≥2^17 are acceptable alternatives for legacy systems. PBKDF2-SHA256 requires ≥ 600,000 iterations (OWASP 2023 minimum). - Enforce password length ≥ 12 characters with no composition rules; allow Unicode; check candidate passwords against a known-breached list (HIBP / pwned-passwords k-anonymity API). - Implement account lockout *or* rate limiting for password attempts (NIST SP 800-63B §5.2.2: at most 100 failures over 30 days). - Implement CSRF protection for state-changing requests reachable from a browser session: synchronizer token, double-submit cookie, or `SameSite=Strict` for high-risk endpoints. - Require MFA / step-up for administrative operations, password changes, MFA-device changes, billing changes. - For OIDC, validate the `nonce` you sent against the `nonce` in the ID token; validate the `at_hash` / `c_hash` when present. ## NEVER - Use `Math.random()` (or any non-CSPRNG) to generate session IDs, reset tokens, MFA recovery codes, or API keys. - Accept JWT `alg=none`; or accept HS256 from a client when the issuer signs with RS256 (classic algorithm-confusion attack). - Compare passwords or token hashes with `==` / `strcmp`; use a constant-time comparator. - Store passwords reversibly (encrypted instead of hashed). Storage must be one-way. - Leak which of username/password was wrong. Return a generic "invalid credentials" message. - Put access tokens, refresh tokens, or session IDs in URL query strings — they leak to logs, Referer headers, and browser history. - Use `localStorage` / `sessionStorage` to hold long-lived refresh tokens. Use HttpOnly cookies. - Trust client-supplied roles / claims at the API layer — re-derive the authenticated subject and look up server-side authorization on each request. - Issue long-lived (>1 hour) access tokens; rely on refresh tokens with rotation. - Use the implicit flow or the password grant. ## KNOWN FALSE POSITIVES - Service-to-service tokens with long TTLs are sometimes acceptable when stored in a secret manager and bound to a specific workload identity. - Local-development "magic link" auth without password hashing for ephemeral dev users is fine if it's gated behind an env flag and disabled in prod. - Tokens in URL query are tolerable in *one* place — the OAuth authorization code return — because the value is short-lived and one-time-use.