54

Auth is where my "ship it" energy usually crashes. I asked for login and got a flow that stored a JWT in localStorage, had no CSRF thought, and logged the user out on every hard refresh because the session check ran client-side only.

The prompt below pins the session to httpOnly cookies, does the auth check in middleware so protected routes are actually protected on the server, and handles the OAuth callback and account-linking edge cases. It's the first generated auth I didn't immediately rip out.

How do you get the model to stop reaching for localStorage tokens by reflex?

THE PROMPT
Implement authentication for a Next.js 14 App Router app with both email/password and one OAuth provider ({PROVIDER}). Security is the priority; do not optimize for brevity.

NON-NEGOTIABLES:
- Sessions live in httpOnly, Secure, SameSite=Lax cookies. NEVER store tokens in localStorage or sessionStorage.
- Passwords hashed with a slow KDF (argon2id or bcrypt with cost >= 12). Never store or log plaintext.
- Route protection happens in middleware.ts on the server, so a protected page never renders for an unauthenticated request. Client checks are UX only, not the gate.
- CSRF protection on all state-changing POSTs.
- OAuth: handle the callback, verify state, and handle account linking when the OAuth email matches an existing email account (don't create a duplicate user).

EDGE CASES to handle explicitly: expired session, revoked/rotated refresh, email already registered, OAuth denied by user, and concurrent logins.

DELIVER: middleware.ts, the auth lib (session create/verify/destroy), the login/register route handlers, and the OAuth callback handler. Strict TypeScript, no any.

End with a threat-model note: for each of session theft, CSRF, and password leak, name the specific line/mechanism that mitigates it.
localStorage tokens by reflex is so real. i now put NEVER-store-tokens-in-localStorage as literally the first bullet and it mostly listens.nullpointer 1 month ago
the per-threat mitigation note at the end is a nice trick, it forces the model to actually reason about why each control is there instead of cargo-culting.typescript_tess 1 month ago
add a comment

1 Answer

23

The "middleware is the gate, client checks are UX only" framing is exactly right and where most generated auth is wrong. One caveat on the threat-model note: make it also address session fixation, rotate the session ID on login and on privilege change. And be careful with SameSite=Lax plus OAuth redirects, the callback is a top-level GET so Lax is fine, but if you ever move to a popup flow you'll need to revisit it.

THE PROMPT
Add to the non-negotiables: rotate the session identifier on every login and on any privilege change to prevent session fixation.

Your Answer