📄 SKILL.md

← Vault

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://.supabase.co/functions/v1/" \

-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://.supabase.co/functions/v1/" \

-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

IssueCauseFix
-------------------
Edge function 401 with service roleLegacy JWT formatUse anon key to call, service role internally
admin.rpc('sql', ...) failsRPC function sql doesn't exist in Supabase CloudUse direct .from().update() or Dashboard
Direct psql to Supabase CloudPassword 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 usersPolicy auth.uid() = owner_id fails when owner_id is NULL or wrongFix 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)