name: lovable-vps-oauth-debug
description: Debug OAuth login loops on Lovable/TanStack Start deployed to VPS — exchangeCodeForSession, .env overwrite, JWT secret rotation
category: devops
Lovable/TanStack Start VPS OAuth Debug
Problem
Google OAuth login on TanStack Start app deployed to VPS loops back to login screen without errors.
Root Causes Found (trial & error)
1. Lovable broker OAuth is dead on VPS
lovable.auth.signInWithOAuth() opens broker.lovable.dev/~oauth/... which returns 404 on VPS.
Fix: Replace with native Supabase signInWithOAuth():
`tsx
await supabase.auth.signInWithOAuth({
provider: "google",
options: { redirectTo: ${window.location.origin}/auth, queryParams: { prompt: "select_account" } }
});
`
2. getSession() alone does NOT exchange OAuth code
When Google redirects back to /auth?code=XXX, getSession() returns no session because the code hasn't been exchanged yet.
Fix: In the /auth route's useEffect, detect ?code= and call exchangeCodeForSession(code) FIRST:
`tsx
useEffect(() => {
const url = new URL(window.location.href);
const code = url.searchParams.get("code");
if (code) {
window.history.replaceState({}, "", url.pathname); // clean URL
supabase.auth.exchangeCodeForSession(code).then(({ data, error }) => {
if (data.session) navigate({ to: "/" });
});
return;
}
supabase.auth.getSession().then(({ data }) => {
if (data.session) navigate({ to: "/" });
});
}, [navigate]);
`
3. Vite rebuilds .env from vite.env.* source files
After npm run build, Vite regenerates .env from vite.env.* files. Any direct edits to .env are LOST.
Fix: Edit the source files (vite.env.production, vite.env.development) or the build will overwrite .env.
4. Anon key returning "Invalid API key" = JWT secret was rotated
If PostgREST returns {"message":"Invalid API key","hint":"Double check your Supabase anon or service_role API key."}, the project's JWT secret was regenerated after the keys were generated.
Fix: Get fresh keys from Supabase Dashboard → Project Settings → API. If the dashboard itself is inaccessible due to broken auth, use the management API to regenerate:
`bash
curl -s -X POST "https://api.supabase.com/v1/projects/{ref}/dashboard/generate-link" \
-H "Authorization: Bearer {SBP_MANAGEMENT_TOKEN}"
`
Auth Flow
1. User clicks "Entrar com Google" → signInWithOAuth() → Google OAuth
2. Google redirects to https://YOURDOMAIN/auth?code=XXX&state=YYY
3. useEffect catches ?code=, calls exchangeCodeForSession(code)
4. Supabase validates code + state + redirect_uri
5. Session stored in localStorage, navigate({ to: "/" })
Key Files
- -
/var/www/documentos/deploy-vps-nodejs/app/src/routes/auth.tsx - -
/var/www/documentos/deploy-vps-nodejs/app/.env(auto-regenerated!) - -
/var/www/documentos/deploy-vps-nodejs/app/vite.env.*(source of .env during build) - - CommercialRS Supabase:
dauftiqcvgaydddoxqhh - - Documents VPS deploy:
/var/www/documentos/deploy-vps-nodejs/app/ - - Port: 3010, systemd service:
documentos.service - - Server:
root@31.97.243.106