📄 SKILL.md

← Vault

name: comercialrs-supabase-auth-debug

description: Debug OAuth login issues on Supabase Cloud + TanStack Start VPS app — SSR crashes, 401 errors, wrong user IDs


ComercialRS Supabase Auth Debugging

Contexto

Deploy Lovable/TanStack Start no VPS (documentos.rochasalesseguros.com.br) com Supabase Cloud (dauftiqcvgaydddoxqhh). OAuth via Google funcionando no browser mas login não persistia.


Problema 1: SSR crash no Node.js (502 Bad Gateway)

Sintoma: Servidor crashava ao fazer redirect OAuth para /. Nginx retornava 502. Systemd mostrava reinicializações frequentes.

Causa raiz: O AuthProvider em __root.tsx chama supabase.auth.getSession() durante SSR. Em contexto Node.js, isso pode quebrar porque localStorage não existe no servidor.

Fix: Adicionar ssr: false em TODAS as rotas que usam auth:

`tsx

export const Route = createFileRoute("/")({

ssr: false,

component: () => ,

});

`

Verificação:

`bash

grep -r "createFileRoute" app/src/routes/ --include="*.tsx" | grep -v node_modules

`


Problema 2: Google OAuth cria user ID diferente do email/password

Sintoma: Login Google mostrava email correto no console, mas todas as chamadas API retornavam 401.

Causa raiz: O usuário existia no Supabase com email/password (ou outro provider) com ID A. Quando fez login Google, o Google criou um NOVO usuário com ID B. O profile e roles estavam atrelados ao ID antigo A.

Fix: Criar profile e roles para o ID do Google:

`sql

-- Encontrar o ID do usuário Google:

-- DevTools > Application > Local Storage > sb-{ref}-auth-token > decodificar JWT (base64)

-- O campo "sub" do payload é o user_id

INSERT INTO profiles (user_id, email, full_name, role)

VALUES ('{google_user_id}', 'email@gmail.com', 'Nome', 'admin');

INSERT INTO user_roles (user_id, role)

VALUES ('{google_user_id}', 'adm');

`


Problema 3: Token expira rápido no implicit flow

Sintoma: Após login, 1-2 requests funcionavam e depois 401.

Causa: O implicit flow (hash #access_token=) não fornece refresh_token de verdade. O access token expira rapidamente.

Fix: PKCE flow (?code=). O exchangeCodeForSession gerencia refresh automaticamente.


Fluxo OAuth Debug Checklist

1. Verificar ssr: false em todas as rotas auth-dependent

2. Verificar user_id: Console → JSON.parse(atob(token.split('.')[1])).sub → comparar com profile user_id no banco

3. Verificar profile existe: SELECT * FROM profiles WHERE email = 'user@email.com'

4. Verificar roles existem: SELECT * FROM user_roles WHERE user_id = 'google_id'

5. Testar em aba anônima para evitar conflito com sessões antigas


Rebuild/Deploy

`bash

cd /var/www/documentos/deploy-vps-nodejs/app

source ~/.nvm/nvm.sh && nvm use 20

npm run build

rm -rf assets && ln -sf dist/client/assets assets

systemctl restart documentos

`

Password Reset via admin-reset-user-password Edge Function

Function location: var/www/comercialrs/supabase/functions/admin-reset-user-password/index.ts

Payload:

`json

{ "user_id": "", "new_password": "

" }</p> <p><code>`</code></p> <p><strong>Returns:</strong> <code>{"ok": true}</code> on success (NOT the user object).</p><p><strong>⚠️ Critical quirk</strong>: Multiple rapid calls can silently fail — password is NOT set even though response says ok:true. ALWAYS verify immediately via REST Auth API:</p><p><code>`</code>python</p> <p>import urllib.request, json</p> <p>anon_key = "eyJh..."</p> <p>data = json.dumps({"email": "user@email.com", "password": "password"}).encode()</p> <p>req = urllib.request.Request(</p> <p> "https://dauftiqcvgaydddoxqhh.supabase.co/auth/v1/token?grant_type=password",</p> <p> data=data, headers={"Content-Type": "application/json", "apikey": anon_key}, method="POST"</p> <p>)</p> <p>with urllib.request.urlopen(req, timeout=15) as resp:</p> <p> result = json.loads(resp.read().decode())</p> <p> print("Login OK:", result["access_token"][:30])</p> <p><code>`</code></p><p><strong>If REST API says invalid credentials but function returned ok:true</strong>: this is a timing bug. Workaround: call function ONCE, immediately verify via REST. If fails, retry with a DIFFERENT new password.</p><p><h2>RLS Policy for Edge Functions Writing to Custom Tables</h2></p><p>Edge Functions write to custom tables (like <code>alert_throttle</code>) as <code>anon</code> role by default (when called with anon key). The <code>service_role</code> key bypasses RLS <em>inside the function</em> but external REST calls still enforce RLS.</p><p><strong>Minimal RLS for INSERT + SELECT by anon:</strong></p> <p><code>`</code>sql</p> <p>CREATE POLICY alert_throttle_all ON public.alert_throttle</p> <p> FOR ALL TO anon USING (true) WITH CHECK (true);</p> <p><code>`</code></p><p><strong>Verify INSERT works (should return 201):</strong></p> <p><code>`</code>python</p> <p>import urllib.request, json</p> <p>data = json.dumps([{"signature": "test", "event_type": "test", "..."}]).encode()</p> <p>req = urllib.request.Request(</p> <p> "https://dauftiqcvgaydddoxqhh.supabase.co/rest/v1/alert_throttle",</p> <p> data=data,</p> <p> headers={"Authorization": f"Bearer {anon_key}", "apikey": anon_key,</p> <p> "Content-Type": "application/json", "Prefer": "return=minimal"},</p> <p> method="POST"</p> <p>)</p> <h1>201 = success, 4xx = RLS blocking it</h1> <p><code>`</code></p><p><h2>Quick Auth Debug Checklist</h2></p><p>1. Query <code>auth.users</code> — check <code>email_confirmed_at</code>, <code>confirmation_token</code>, <code>last_sign_in_at</code></p> <p>2. If <code>email_confirmed_at IS NULL</code>: <code>UPDATE auth.users SET email_confirmed_at = now() WHERE id = '...';</code></p> <p>3. If login still fails after reset: test via REST Auth API directly (bypasses UI)</p> <p>4. Check <code>profiles</code> table has matching record for the user</p><p><hr></p><p><h2>Projeto</h2></p> <ul><li>- Supabase: <code>dauftiqcvgaydddoxqhh</code></li> <li>- VPS: 31.97.243.106, app em <code>/var/www/documentos/deploy-vps-nodejs/app</code></li> <li>- URL: <code>https://documentos.rochasalesseguros.com.br</code></li></ul> </article> </div> </body></html>