| name | authentication-and-identity |
| description | Load when designing or reviewing login, sessions, tokens, OAuth/OIDC/SAML flows, passkeys/WebAuthn, MFA, multi-tenant identity, or B2B SSO/SCIM — or when auditing auth code for holes like missing state, open redirects, JWT misuse, or token-storage mistakes. |
Authentication and Identity
The standard doctrine, compressed
A strong model already produces this cold; anchors only. OAuth is delegation, OIDC bolts authentication on (never log users in from a bare access token + /userinfo — token-substitution; require an ID token with your aud); code+PKCE for everything interactive (OAuth 2.1 still an IETF draft as of mid-2026 but de-facto: PKCE mandatory for all clients, implicit/ROPC removed, exact-match redirect URIs); never hand-roll flows (certified libs: openid-client, Authlib, AppAuth; system browser, never webviews). Sessions: monolith → server-side session + httpOnly cookie, do NOT add JWTs; many services → 5–15 min JWT access + rotating refresh token, reuse-of-rotated-token = theft signal → revoke the family; SPA → BFF preferred, else access token in JS memory + refresh in httpOnly cookie; never localStorage. JWT validation: pinned algorithm allowlist (RS256→HS256 public-key-as-HMAC confusion), iss/aud/exp required, JWKS by kid. Passkeys: origin-bound = structurally phishing-resistant; the hard problem is recovery, ladder from second authenticator → hardware key → hashed one-time recovery codes → IDV re-proofing → email (which caps account security at email strength); helpdesk resets are the breach vector (MGM-class). MFA ranking per NIST 800-63B-4 (2025): WebAuthn > number-matching push > TOTP (proxy-phishable) > SMS (formally restricted). Identity key = (iss, sub), never email (recycled, mutable, sometimes unverified — and never auto-link accounts on email match without email_verified and issuer trust). SCIM's product value is the deactivate path (JIT has no offboarding story); DPoP (RFC 9449) sender-constrains tokens for money-moving APIs. SAML: hardened library, signed assertions, InResponseTo, per-tenant issuer/cert pinning (a tenant's IdP must not mint assertions for another tenant).
Sharpenings the baseline states weakly
- SCIM deactivate must kill live state, not just the row. Everyone knows SCIM = enterprise offboarding; the miss is wiring: a SCIM
active:false must revoke refresh-token families and terminate gateway/BFF sessions server-side, or the "deprovisioned" user browses on for hours. Write that handler when you build the endpoint — it is the entire point of the purchase. Corollary: JIT-provisioned users need sessions re-evaluated against user status server-side for the same reason.
- Block password login for SSO-enforced domains — otherwise the CISO's Okta policy is bypassed by "forgot password." Enforce at the realm/tenant level, and prefer forcing SP-initiated SAML (IdP-initiated has no
InResponseTo binding — a standing gift to assertion injection).
- Federated MFA: require
amr/acr in the connection contract. "Enterprise users bring their IdP's MFA" is only true if you demand those claims; respect them to avoid double-prompting, but a connection that omits them silently downgrades assurance.
- API keys are passwords with different ergonomics. ≥256-bit random, store only a hash (plain SHA-256 is fine here — high-entropy input, unlike passwords), show once, allow ≥2 concurrently active keys so rotation is overlap-and-retire instead of a coordinated outage. Prefix with an identifiable marker (
sk_live_, yourco_) — that prefix is what makes GitHub push protection and trufflehog catch leaks; register the format with GitHub's secret-scanning program if you're a platform. Track last_used_at (the key idle 9 months should alarm before an attacker uses it). Keys check a store at request time — that instant revocability is their one advantage over JWTs; don't cache validation for an hour and throw it away.
- Lifetime table to defend in review: access 5–15 min; refresh days–weeks sliding with rotation, absolute cap 30–90 days; ID token minutes (login evidence, not an API credential — and APIs must reject ID tokens: wrong
aud, wrong semantics); session cookie ≤ working day sliding for sensitive apps; reset tokens ≥128-bit, hashed at rest, 15–60 min, single-use, all sessions revoked on success, uniform response regardless of account existence.
- Small-but-fatal checklist (each one line because completeness is the point): per-request
state bound to the browser session (login CSRF — PKCE does not replace it); exact-match redirect_uri and relative-path-only ?next=; session ID regenerated at every privilege change (fixation); hmac.compare_digest/timingSafeEqual for every secret comparison; TOTP seeds KMS-encrypted and recovery codes hashed (not plaintext next to the password hashes they back up); no tokens in URLs (logs, history, Referer — anything that must be a link is single-use and short-lived because it will leak); CORS credentials mode = exact-origin allowlist, never reflected origin.
Worked spine (the decisions, not the ceremony)
B2B SaaS, React SPA + APIs, Okta SSO for enterprises, email/password for SMBs: pick an IdP (Keycloak/Auth0), your app is a client of one issuer; enterprise IdPs federate into it per tenant, so you speak OIDC once. org_id claim minted at login after home-realm discovery (verify domain ownership via DNS TXT before enabling domain auto-join); a user in two orgs gets different tokens per org, never ambient dual access. BFF at the existing gateway: browser holds a session cookie only. Rejected long-lived access tokens ("saves refresh traffic") — a 24h unrevocable token turns any XSS or log leak into a day-long breach; the refresh hop is the revocation story. Recovery: codes at enrollment; email reset allowed for standard users, disabled for org admins (helpdesk-verified re-proofing instead). Verification drill before shipping: (1) revocation drill — fire an employee on paper, enumerate every live credential and its latency-to-death; anything unrevocable >15 min is a finding; (2) phish it on paper — run an Evilginx proxy through each login and recovery path, only origin-bound paths survive; then run the helpdesk attack ("lost my phone, forgot my password"); (3) tenancy cross-check — replay tenant A's token and tenant A's IdP assertion (claiming a tenant-B email) against tenant B; both must fail closed.
Verification / self-check
- Revocation drill (above) with concrete latencies per credential type.
- Callback checklist: exact-match
redirect_uri; state generated/bound/verified; PKCE end-to-end; iss/aud/exp/nonce; algorithm pinned; JWKS by kid with rotation handling.
- Storage audit: where every token/secret rests — browser mechanism, server encryption, logs (Authorization headers and URLs scrubbed?), crash reports.
- Stopping rule: certified library + code/PKCE + short access/rotating refresh + httpOnly-or-BFF + green callback checklist + written recovery ladder = done. Further cleverness (custom crypto, exotic token schemes) adds attack surface faster than assurance — spend remaining effort on recovery, helpdesk procedure, and offboarding latency.
Delta notes (vs Opus 4.8 baseline, audited 2026-07)
- Probed 13 claims: 13 baseline (cut/compressed — SPA/BFF architecture, rotation + family revocation, token-substitution attack, OAuth 2.1 draft status, NIST 800-63B-4 incl. restricted SMS, passkey recovery ladder + helpdesk caveat, email auto-link vuln, (iss,sub) keying, SCIM deactivation, MFA ranking, JWT checks + RS256/HS256, DPoP RFC 9449, SAML multi-tenant pitfalls), 0 partial, 0 hard deltas. Strongest-baseline skill in the audited set.
- Biggest baseline gaps (all behavioral, not factual): Opus states SCIM-deactivate but not the live-session/refresh-family kill wiring; omits password-login bypass of SSO enforcement and the
amr/acr contract requirement; API-key operational design (prefix→secret-scanning, overlap rotation, last_used_at) present only in fragments.
- File restructured as a correction sheet: doctrine compressed to anchors; retained weight sits on wiring details and the drill-style verification.