Add NEXT_PUBLIC_GARY_SKIP_AUTH dev/demo bypass
From the PR description
Summary
Adds an environment-gated dev/demo bypass so the app can be inspected without Supabase login. Frontend-only. No auth code is removed; login/signup screens and the protected-route gate stay intact.
How auth currently works
AuthProvider(root, viaProviders) callssupabase.auth.getSession()on mount, setsUser { id, email }, listens toonAuthStateChange, and POSTs to/user/profileto ensure the backend profile row.- Protected gate:
(pages)/layout.tsx:62-65redirects to/loginwhen!authLoading && !isAuthenticated. /loginand/signupredirect to/assistantonceisAuthenticated.- Backend-hitting components read
useAuth().userfor id/email and callsupabase.auth.getSession()directly to attach the bearer token.
Where the bypass was added
frontend/src/contexts/AuthContext.tsx- module-levelSKIP_AUTH = process.env.NEXT_PUBLIC_GARY_SKIP_AUTH === "true". LazyuseStateinitializers seed the demo user when on.useEffectbails out after a one-timeconsole.warn.signOut()no-ops when on. The flag is exposed on the context asisAuthBypassed.frontend/src/components/dev-auth-banner.tsx- new slim amber strip that says "Dev auth bypass enabled".frontend/src/app/(pages)/layout.tsx- renders<DevAuthBanner />at the top of the layout whenisAuthBypassedis true.frontend/.env.local.example- documents the variable (commented out by default).
Fake user shape
{
id: "00000000-0000-0000-0000-000000000000",
email: "demo@gary.local",
}
UUID-shaped so anything that string-coerces or hashes the id behaves predictably. The email.split("@")[0] fallback in InitialView produces a sensible greeting ("Hi, demo") when no profile exists.
How to enable
Local dev
# in frontend/.env.local
NEXT_PUBLIC_GARY_SKIP_AUTH=true
Then npm run dev. The login screen redirects straight into /assistant, the amber banner shows, and the home renders without needing a real Supabase project.
Deployed Cloudflare Worker
NEXT_PUBLIC_* is baked at build time by OpenNext. Set the var in the build environment before npm run cf:build && npm run cf:deploy, or in the Cloudflare Worker's build configuration. Use a separate Worker (e.g. a preview/staging deploy) for this so production keeps real auth.
Off path verified unchanged
- Lazy
useState(() => SKIP_AUTH ? DEMO_USER : null)evaluates tonullwhen the flag is unset, exactly like today. - Lazy
useState(() => !SKIP_AUTH)evaluates totrue, exactly like today. - The
useEffectbody'sif (SKIP_AUTH)branch is dead; the existingcheckUser()+onAuthStateChange()block runs as before. signOut()'s early-return is dead; it still callssupabase.auth.signOut()and clears the user.(pages)/layout.tsxonly renders<DevAuthBanner />whenisAuthBypassedis true -false && ...collapses to nothing.- Login/signup pages are untouched.
Limitations
- Backend calls 401 while the bypass is on. No real Supabase session exists, so
supabase.auth.getSession()returns null and the bearer token isn't attached. The console warning calls this out. The bypass is for inspecting the UI, not for exercising the backend. /user/profilePOST is skipped while on. Wouldn't succeed without a token anyway.signOutis intentionally a no-op while on. Without it, the next mount would re-create the demo user and the user would seem to bounce. Cleaner to just keep the demo session stable.- Build-time toggle on Cloudflare deploys. OpenNext bakes
NEXT_PUBLIC_*at build time, so flipping the var without rebuilding has no effect on the deployed Worker.
Tests
There are no auth-gating tests in this repo, and no test scripts in frontend/package.json or backend/package.json. Nothing to update.
Build / lint
npx tsc --noEmit- clean.npm run lint- 107 problems onmain, 107 after (initial draft introduced one newreact-hooks/set-state-in-effect; refactored to lazyuseStateinitializers and bypassreturnin the effect, which restores the baseline).npm run build- samefonts.googleapis.comsandbox network failure onmainand on this branch; environmental. Workers Builds will run the real build on the PR.
Test plan
- Workers Build for this PR succeeds.
- On the preview Worker URL without the env var: log in normally, redirect to
/assistant, no amber banner. Sign out works. - In a separate preview build with
NEXT_PUBLIC_GARY_SKIP_AUTH=true: opening the root URL lands on/assistantwithout going through/login; the amber "Dev auth bypass enabled" banner is visible at the top; the four Gary-mode cards render. - In bypass mode, navigate to
/loginmanually - it should redirect away to/assistantbecauseisAuthenticatedis true. - In bypass mode, click Sign Out in the sidebar dropdown - nothing happens (no-op), session stays stable.
- Console warning fires once per session in bypass mode.
https://claude.ai/code/session_019pRkhcGDRKQWHjzAnV5yCL
Generated by Claude Code
Our analysis
Environment-gated dev auth bypass for Gary — read the full analysis →
Think the analysis missed something the PR description covers?
Commits in this PR (1)
| SHA | Subject | Author | Date | |
|---|---|---|---|---|
0b9ad4db | Add NEXT_PUBLIC_GARY_SKIP_AUTH dev/demo bypass | Claude | 2026-05-07 | ↗ GitHub |
commit bodyLets us inspect the app without going through Supabase login during
development and on demo deploys. Frontend-only.
When NEXT_PUBLIC_GARY_SKIP_AUTH=true:
- AuthContext initializes with a fake demo user
({ id: "00000000-...-000", email: "demo@gary.local" }) via lazy
useState, so isAuthenticated is true immediately.
- The Supabase session check + auth-state-change subscription are
skipped (the useEffect bails out after a one-time console.warn).
- signOut becomes a no-op so the demo session stays stable.
- A slim amber "Dev auth bypass enabled" banner renders at the top
of the (pages) layout via the new DevAuthBanner component.
- The flag is exposed as `isAuthBypassed` on the auth context so
any consumer can branch on it later.
When the flag is unset or "false":
- useState initializers return null/true, identical to today.
- The effect's bypass branch is dead and the original Supabase
logic runs unchanged. Login/signup, signOut, and the redirect
gate in (pages)/layout.tsx are untouched.
Documents the variable in frontend/.env.local.example as
commented-out so normal setups stay on real auth.
No backend changes, no schema changes, no auth contract changes.
Backend calls made while the bypass is on will 401 because no
real Supabase session exists - that's intentional and noted in
the console warning.
https://claude.ai/code/session_019pRkhcGDRKQWHjzAnV5yCL
| ||||
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-11.md from
inside the repo you want the changes in.