name: supabase-edge-function-auth-debug
description: Debug 401/403 errors in Supabase Edge Functions — JWT auth, anon key vs service role, and why RPC calls fail
category: supabase
Supabase Edge Functions — Auth Debug
The Problem
Edge Functions return 401 Unauthorized or Invalid JWT when called with service role key, but work with anon key.
Root Cause
Supabase Edge Functions use GoTrue JWT validation. The service role key (sb_...) is a legacy JWT format that GoTrue rejects. The anon key (eyJ...) is the correct format for edge function auth.
How to Call Edge Functions
✅ Works (anon key)
`bash
ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X POST "https://
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"your_action"}'
`
❌ Fails (service role key)
`bash
Returns: {"code":"UNAUTHORIZED_LEGACY_JWT","message":"Invalid JWT"}
curl -X POST "https://
-H "Authorization: Bearer $SERVICE_ROLE_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"your_action"}'
`
Key Insight
Inside the edge function, createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY) uses the service role internally (bypasses RLS). But the incoming request auth is validated against GoTrue, which only accepts the anon key JWT format.
So: call the function with anon key, but the function itself uses service role internally for admin operations.
Related Issues
| Issue | Cause | Fix |
| ------- | ------- | ----- |
| Edge function 401 with service role | Legacy JWT format | Use anon key to call, service role internally |
admin.rpc('sql', ...) fails | RPC function sql doesn't exist in Supabase Cloud | Use direct .from().update() or Dashboard |
| Direct psql to Supabase Cloud | Password auth uses scram-sha-256, password is the service role key JWT (wrong) | Can't connect directly; use REST API or Dashboard |
| RLS blocks authenticated users | Policy auth.uid() = owner_id fails when owner_id is NULL or wrong | Fix via Dashboard or update owner_id to match auth.uid() |
RLS Debugging Pattern
1. Call diagnose action via edge function (uses service role internally = full access)
2. Check owner_id column — NULL or wrong ID = blocked by RLS
3. Fix options:
- Option A: Dashboard → tables/clients/policies → change auth.uid() = owner_id to true
- Option B: Update owner_id to match auth.uid() of a real user (but this only helps that one user)
- Option C: Create client_access table linking users to clients
For DocCorretor (dauftiqcvgaydddoxqhh)
- - Edge function:
migrate-drive— accepts anon key, uses service role internally - - Actions:
diagnose,insert_docs,fix_names,create_clients_bulk,fix_owner_ids - - RLS issue:
clientstable hasauth.uid() = owner_idpolicy, but most clients haveowner_id = NULL - - Fix: Dashboard → clients table → RLS policies → allow all authenticated users