Session expires immediately
appears when:user signs in, lands on the dashboard for a split second, then gets kicked back to /login.
Session expires immediately
Login appears to succeed — and then the very next request reports the user as signed out. Three causes account for nearly every case: blocked cookie, broken JWT timestamp, or a signing secret that changes between lambdas.
Session expires immediately because of one of three patterns: SameSite=Strict blocks the cookie on the OAuth cross-site redirect, the Secure flag is set over HTTP and the browser silently drops the cookie, or JWT exp is in milliseconds instead of seconds so the token expires the instant it is issued. Check the cookie attributes in devtools → Application → Cookies, decode the JWT at jwt.io, then align auth.config.ts. Same-day fix.
Quick fix for session expires immediately
01// auth.config.ts — correct cookie options for production02export const config = {03 cookies: {04 sessionToken: {05 name: 'next-auth.session-token',06 options: {07 httpOnly: true,08 sameSite: 'lax', // NEVER 'strict' for OAuth flows09 secure: true, // required over HTTPS10 path: '/',11 // domain: '.yourapp.com', // only when sharing across subdomains12 },13 },14 },15 session: {16 strategy: 'jwt',17 maxAge: 30 * 24 * 60 * 60, // 30 days, in seconds18 },19};20 21// JWT exp math — seconds, not milliseconds22const exp = Math.floor(Date.now() / 1000) + 60 * 60; // 1hDeeper fixes when the quick fix fails
01 · Lock NEXTAUTH_SECRET across every Vercel lambda
If the env var is unset, Auth.js falls back to an auto-generated per-instance secret. A token signed on lambda A fails verification on lambda B. From the user's point of view, they are "logged out randomly" — actually they are being routed to a different lambda with a different secret. Set NEXTAUTH_SECRET explicitly in Vercel Production and never rotate it casually.
01# Generate once, commit nothing, paste into Vercel Settings → Environment Variables02openssl rand -base64 3203# NEXTAUTH_SECRET=<that value>04# NEXTAUTH_URL=https://yourapp.com02 · Fix JWT timestamp math (the off-by-1000 bug)
AI generators sometimes produce exp: Date.now() + 3600, which is milliseconds plus seconds. The token expires 3,600 seconds before now and the server rejects it on first use. Always divide Date.now() by 1000 and add a positive offset.
01// WRONG — milliseconds + seconds02const exp = Date.now() + 3600;03 04// WRONG — no offset05const exp = 3600;06 07// CORRECT — seconds + seconds08const exp = Math.floor(Date.now() / 1000) + 60 * 60;03 · Switch Supabase to the SSR client on App Router
The default Supabase browser client stores sessions in localStorage. Server components on Next.js 16 App Router can only read cookies, so every protected-route render sees no session and bounces to /login. The fix is @supabase/ssr with a shared cookie jar.
01import { createServerClient } from '@supabase/ssr';02import { cookies } from 'next/headers';03 04export async function getSupabase() {05 const store = await cookies();06 return createServerClient(07 process.env.NEXT_PUBLIC_SUPABASE_URL!,08 process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,09 {10 cookies: {11 getAll: () => store.getAll(),12 setAll: (all) => all.forEach((c) => store.set(c)),13 },14 },15 );16}04 · Handle subdomain session sharing
If the marketing site runs on yourapp.com and the app on app.yourapp.com, a default cookie is scoped to one host. Set domain: '.yourapp.com' to share the session across subdomains.
Why AI-built apps hit session expires immediately
Lovable, Bolt, and Cursor scaffold auth that stores sessions in a JWT cookie. The generator applies default cookie options that are fine on localhost — SameSite=Lax or sometimes SameSite=Strict, no Secure flag, no specific Domain. Local dev works. Once deployed, the browser enforces stricter rules: SameSite=Strict blocks cookies on any cross-site redirect, and OAuth flows are cross-site by definition. The cookie is set correctly by the callback, but the very next request — the redirect back to the dashboard — doesn't include it, and the app treats the user as logged out.
The second pattern is a JWT timestamp bug. AI generators sometimes produce code like exp: Date.now() + 3600 — which is milliseconds plus seconds, producing an exp value 3,600 seconds before now. The library reads exp as a Unix timestamp in seconds, sees the value is in the past, and rejects the token immediately. The correct construction is Math.floor(Date.now() / 1000) + 60 * 60. This kind of off-by-1000 error is hard to spot because the code compiles and runs without warning.
The third pattern is a NEXTAUTH_SECRET that changes on every deploy. Auth.js uses this secret to sign and verify the session JWT. If it is not set explicitly, Auth.js auto-generates a per- instance fallback at startup. A token signed by instance A fails verification on instance B, so the user is "logged out" on every request that happens to land on a different lambda. The symptom looks like a session ttl problem but the cause is a signing-secret mismatch.
session expires immediately by AI builder
How often each AI builder ships this error and the pattern that produces it.
| Builder | Frequency | Pattern |
|---|---|---|
| Lovable | High | Supabase client uses localStorage by default; server never sees the session. |
| Bolt.new | High | Auth.js scaffold omits NEXTAUTH_SECRET; per-lambda fallback rotates between cold starts. |
| v0 | Medium | Ships cookie options without the Secure flag; HTTPS browsers drop the cookie. |
| Cursor | Medium | JWT exp written as Date.now() + duration (milliseconds + seconds). |
| Replit | Medium | Preview URL leaked into cookie domain; breaks when moved to a custom host. |
| Claude Code | Low | Correct maxAge but no session refresh — token expires without rotation. |
| Windsurf | Low | Uses SameSite=Strict 'for security'; blocks cookie on OAuth cross-site redirect. |
| Base44 | Medium | Stores session in localStorage only; Safari ITP evicts it between tabs. |
Related errors we fix
Stop session expires immediately recurring in AI-built apps
- →Default to SameSite=Lax for any OAuth-based session cookie; never Strict.
- →Use a single utility to build JWT exp so the milliseconds/seconds trap cannot recur.
- →Pin NEXTAUTH_SECRET once; never rotate during a hotfix window.
- →Run a post-deploy script that logs in, waits 10 seconds, and asserts the session persists.
- →Test every release in Safari and Firefox — ITP and ETP catch bugs Chrome forgives.
Still stuck with session expires immediately?
Keep users signed in — we fix session bugs in the same-day window.
- →Aligns cookie flags, JWT math, and NEXTAUTH_SECRET together
- →Works across Auth.js, Supabase, Clerk, and custom handlers
- →Shipped with a post-deploy smoke test so it can't silently recur
session expires immediately questions
Why does my session expire immediately after logging in?+
How do I inspect the session cookie in the browser?+
What is the correct JWT exp format?+
Does SameSite=Strict break OAuth sessions?+
How do I keep users signed in across refreshes after a login?+
Ship the fix. Keep the fix.
Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.
Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.
session expires immediately experts
If this problem keeps coming back, you probably need ongoing expertise in the underlying stack.