Fixing Supabase auth in vibe-coded apps
Auth is where AI-generated apps break most often. Login works once, then loops. Magic links land on localhost. Users sign up but can't read their own data. Here's how to fix each of these.
1. Redirect URLs point to localhost
In Supabase, open Authentication → URL Configuration. Set the Site URL to your production domain and add every preview URL to Redirect URLs (wildcards like https://*-you.vercel.app/** work). Pass emailRedirectTo explicitly:
await supabase.auth.signInWithOtp({
email,
options: { emailRedirectTo: `${window.location.origin}/auth/callback` }
})
2. The login loop
This usually means the app checks the session before Supabase has restored it. Listen for auth state changes instead of reading the session once:
useEffect(() => {
const { data } = supabase.auth.onAuthStateChange((_e, session) => {
setSession(session); setLoading(false)
})
return () => data.subscription.unsubscribe()
}, [])Don't redirect while loading is still true.
3. "new row violates row-level security policy"
RLS is on (good), but no policy lets users write their own rows. Add one:
create policy "own rows" on public.profiles for all using (auth.uid() = user_id) with check (auth.uid() = user_id);
Never "fix" this by disabling RLS or shipping the service_role key to the browser.
4. Next.js SSR sessions disappear
In the App Router, use @supabase/ssr with cookie-based clients and a middleware that refreshes the session. A browser-only client won't see the session on the server.
5. OAuth works locally but not in production
Add the Supabase callback URL (https://<project>.supabase.co/auth/v1/callback) to the Google or GitHub OAuth app, and enable the provider in Supabase with production credentials.
Security checklist
- Only the anon key in client code
- RLS enabled on every table, with explicit policies
- Redirect allow-list kept tight
- Email confirmation enabled for production