📄 SKILL.md

← Vault

name: supabase-edge-runtime-jwt-debug

description: Debug 401 Unauthorized in Supabase self-hosted Edge Functions — SERVICE_ROLE_KEY vs JWT_SECRET for token verification


Supabase Self-Hosted: Edge Function 401 Debug

Root Cause

In Supabase self-hosted (VPS), Edge Functions use TWO different JWT verification paths:

1. Built-in Supabase JWT verification — controlled by VERIFY_JUT: "false" in docker-compose. When true, GoTrue validates tokens before passing to the function. Our setup has this disabled.

2. Custom inline JWT verification — the function code calls verifyTokenInline() using crypto.subtle.verify() (HMAC-SHA256). This uses a key to verify the signature.

The Bug

`typescript

// ❌ WRONG — SERVICE_ROLE_KEY is Supabase's internal service key

const SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;

const key = new TextEncoder().encode(SERVICE_ROLE_KEY);

// ✅ CORRECT — JWT_SECRET is what GoTrue uses to sign user tokens

const JWT_SECRET = Deno.env.get("JWT_SECRET")!;

const key = new TextEncoder().encode(JWT_SECRET);

`

User tokens (from /auth/v1/token?grant_type=password) are signed by GoTrue using JWT_SECRET. The SERVICE_ROLE_KEY is a completely different key (has iss: "supabase", role: "supabase_admin"). Using the wrong key for signature verification causes ALL tokens to fail with 401 Unauthorized.

Debugging Steps

`

1. docker exec deploy-vps-functions-1 env | grep JWT_SECRET → get actual JWT_SECRET

2. docker exec deploy-vps-functions-1 cat /home/deno/functions/main/index.ts | grep -n SERVICE_ROLE_KEY

3. Decode user token: curl -s -X POST 'https://site.com/auth/v1/token?grant_type=password' \

-H 'apikey: ANON_KEY' -d '{"email":"...","password":"..."}' | python3 -c '...'

→ Check: token has NO aud/iss claims, only sub/email/role

4. Decode SERVICE_ROLE_KEY: echo $SERVICE_ROLE_KEY | cut -d. -f2 | base64 -d

→ Has iss: "supabase", role: "supabase_admin", is_super_admin: true

5. Fix: change SERVICE_ROLE_KEY → JWT_SECRET in verifyTokenInline

`

Files to Update

When fixing the SERVICE_ROLE_KEY → JWT_SECRET bug in verifyTokenInline:

Changes to /opt/rochasales/supabase/ edge-runtime/functions ONLY affect rochasalesseguros.com.br. DocCorretor uses a separate build and .env with different Supabase URL.