name: supabase-auth-flow-nginx-debug
description: Debug Supabase auth login flash-loops by reading nginx access logs to trace the exact API sequence the browser uses, identifying which POST-login verification check fails and causes the 204 logout.
Supabase Auth Flow Debug via Nginx Logs
Debug why a Supabase-based frontend app logs out immediately after successful login.
The Problem
Frontend login appears to succeed (auth returns 200) but browser immediately gets logged out (204 on logout endpoint). The user sees a "flash" of logged-in state then returns to login page.
Root Cause Pattern
The frontend does post-login verification checks before confirming the session:
1. Login succeeds (200)
2. Frontend calls GET /rest/v1/profiles?is_active=eq.true → if 400, logout
3. Frontend calls GET /rest/v1/user_roles?user_id=eq.{id}&role=eq.admin → if 400, logout
4. Frontend calls GET /rest/v1/custom_roles?has_admin_access=eq.true → if 400, logout
If ANY of these fail with 400, the frontend calls POST /logout immediately.
Diagnosis Technique
Step 1: Identify the exact API sequence the browser makes
Read the nginx access log on the VPS (not the auth container log):
`bash
sshpass -p '$PASS' ssh root@$VPS_IP \
"tail -200 /var/log/nginx/access.log | grep -E '(custom_roles|user_roles|profiles|logout|token)'"
`
Look for the pattern:
- -
POST /token→ 200 (login) - -
GET /rest/v1/profiles→ 400 (profile check fails) - -
GET /rest/v1/user_roles→ 400 (role check fails) - -
POST /logout→ 204 (frontend logs out because check failed) - - Test from outside:
curl -s -w '\nHTTP_CODE:%{http_code}' -X POST https://domain.com/auth/v1/token -H "Content-Type: application/json" -d '{"grant_type":"password","email":"...","password":"..."}' - - If curl returns 200 but browser returns 400 → the issue is Kong CORS or browser-specific (extensions, cache, cookies)
- -
curl -X POST https://domain.com/auth/v1/token -H "Content-Type: application/json" -d '{"grant_type":"password","email":"...","password":"..."}'returns 200 - -
curl -X POST https://domain.com/auth/v1/token?grant_type=password -H "Content-Type: application/json" -d '{"email":"...","password":"..."}'returns 200 - - DocCorretor login (same GoTrue + Kong) works for the user
Step 2: Test each failing endpoint with curl
Use the user's Bearer token (from auth container logs after login):
`bash
Get token from auth logs
docker logs deploy-vps-auth-1 --tail 500 2>&1 | grep -o '"access_token":"[^"]*"'
Test each endpoint
curl -s "https://rochasalesseguros.com.br/rest/v1/user_roles?user_id=eq.{uuid}&role=eq.admin" \
-H "Authorization: Bearer $TOKEN" \
-H "apikey: $ANON_KEY"
`
Step 3: Identify exact 400 reason
Use the PostgREST container to test as authenticated role:
`bash
docker exec deploy-vps-db-1 psql -U postgres -d postgres -t -c \
"SET ROLE authenticated; SELECT * FROM user_roles LIMIT 1;"
`
Common Root Causes
1. Missing columns the frontend expects
Frontend queries user_roles with columns user_id and role:
`sql
-- WRONG: user_roles was created as a metadata table
CREATE TABLE user_roles (id UUID, role_name TEXT, permissions JSONB);
-- CORRECT: user_roles is a junction table
CREATE TABLE user_roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users NOT NULL,
role TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(user_id, role)
);
`
Frontend queries custom_roles with column has_admin_access:
`sql
ALTER TABLE custom_roles ADD COLUMN has_admin_access BOOLEAN DEFAULT false;
UPDATE custom_roles SET has_admin_access = true WHERE name = 'Administrador';
`
2. RLS blocking authenticated role
`sql
-- Check current policies
SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check
FROM pg_policies WHERE tablename = 'user_roles';
-- Fix: ensure authenticated can read
DROP POLICY IF EXISTS user_roles_select ON user_roles;
CREATE POLICY user_roles_select ON user_roles FOR SELECT TO authenticated USING (true) WITH CHECK (true);
`
3. auth schema not accessible to authenticated role
`sql
GRANT USAGE ON SCHEMA auth TO authenticated;
GRANT ALL ON ALL TABLES IN SCHEMA auth TO authenticated;
`
Then restart PostgREST:
`bash
docker exec deploy-vps-rest-1 kong reload
`
4. PostgREST schema not reloaded after table creation
`bash
Reload PostgREST schema
docker exec deploy-vps-rest-1 kong reload
Wait 5 seconds then test
sleep 5 && curl -s "https://domain.com/rest/v1/table_name" -H "apikey: $ANON_KEY"
`
Kong Configuration Debug (Critical)
Kong kong reload does NOT fully reload config
Kong's kong reload sends SIGTERM to workers, but nginx workers preserve their original config state. Declarative config in /tmp/kong.yml is NOT fully reloaded by kong reload. Must use docker restart for config changes to take effect.
`bash
WRONG - config not fully reloaded
docker exec deploy-vps-kong-1 kong reload
CORRECT - full restart needed
docker restart deploy-vps-kong-1
`
Kong container config vs host config divergence
Kong's declarative config inside the container (/tmp/kong.yml) can diverge from the host file because kong reload doesn't properly re-read it. Always verify:
`bash
diff /tmp/kong.yml <(docker exec deploy-vps-kong-1 cat /tmp/kong.yml)
`
If they differ after a kong reload, the container still has stale config. Use docker restart.
Kong DNS resolution failure in Docker networks
Kong's nginx uses Docker's embedded DNS at 127.0.0.11. Container hostnames (like functions) are NOT resolvable by Kong unless:
1. The container's name matches the hostname (not just the service name in docker-compose)
2. Or use an intermediate proxy container with socat
Workaround - create a proxy container:
`bash
docker run -d --name deploy-vps-functions-proxy \
--network deploy-vps_default \
alpine/socat TCP-LISTEN:9000,fork,reuseaddr TCP:deploy-vps-functions-1:3000
`
Then in kong.yml:
`yaml
services:
- name: functions
url: http://deploy-vps-functions-proxy:9000 # NOT "functions:3000"
`
Getting container IPs:
`bash
docker network inspect deploy-vps_default --format '{{json .Containers}}' | python3 -c "import json,sys; [print(c['Name'], c['IPv4Address']) for c in json.load(sys.stdin)['Containers'].values()]"
`
Kong CORS credentials + wildcard origin conflict
When browser sends credentials: true (cookies, Authorization headers), Access-Control-Allow-Origin cannot be * — it must be an explicit origin. Chrome rejects this with a CORS error OR the server responds with 400.
WRONG CORS config (causes 400 in browser):
`yaml
plugins:
- name: cors
config:
origins: ["*"] # FAILS with credentials: true
credentials: true # Browser sends cookies
`
CORRECT CORS config:
`yaml
plugins:
- name: cors
config:
origins:
- https://rochasalesseguros.com.br
credentials: true
`
Verify Kong is the problem vs GoTrue
Server-side tests (curl) work but browser gets 400:
Browser 400 on auth endpoint but curl works
All curl tests return 200 but browser gets 400 on POST /auth/v1/token. This is NOT a GoTrue or Supabase problem — the backend is working. The issue is browser-specific:
Most likely causes (in order):
1. Browser extensions blocking/modifying requests (AdBlock, Privacy Badger, etc.)
2. Stale minified JS bundle in browser cache
3. Browser sending cookies that corrupt the request body
4. Browser sending malformed headers
Diagnosis steps:
1. Ask user to try incognito/private window
2. Ask user to try from a different browser/device
3. Check browser console Network tab for exact request headers being sent
4. Clear browser cache and hard reload (Ctrl+Shift+R)
Server-side is confirmed working when:
Quick Fix Checklist
`bash
1. Grant auth schema permissions
docker exec deploy-vps-db-1 psql -U postgres -d postgres -c \
"GRANT USAGE ON SCHEMA auth TO authenticated; GRANT ALL ON ALL TABLES IN SCHEMA auth TO authenticated;"
2. Ensure RLS policies allow authenticated SELECT
docker exec deploy-vps-db-1 psql -U postgres -d postgres -c \
"DROP POLICY IF EXISTS user_roles_select ON user_roles; \
CREATE POLICY user_roles_select ON user_roles FOR SELECT TO authenticated USING (true) WITH CHECK (true);"
3. Add missing columns
docker exec deploy-vps-db-1 psql -U postgres -d postgres -c \
"ALTER TABLE custom_roles ADD COLUMN IF NOT EXISTS has_admin_access BOOLEAN DEFAULT false;"
4. Reload PostgREST schema
docker exec deploy-vps-rest-1 kong reload
`
Verification
After fixes, confirm all endpoints work with curl:
`bash
TOKEN="..." # from auth logs
ANON_KEY="..."
curl -s "https://domain.com/rest/v1/profiles?select=id,user_id,is_active" \
-H "Authorization: Bearer $TOKEN" -H "apikey: $ANON_KEY"
curl -s "https://domain.com/rest/v1/user_roles?select=role&user_id=eq.{uuid}" \
-H "Authorization: Bearer $TOKEN" -H "apikey: $ANON_KEY"
curl -s "https://domain.com/rest/v1/custom_roles?select=id,name,has_admin_access" \
-H "Authorization: Bearer $TOKEN" -H "apikey: $ANON_KEY"
`
All should return 200 with data, not 400.