Skip to main content

Source: ocean/docs/adr/005-supabase-jwt-authentication.md | ✏️ Edit on GitHub

ADR-005: Supabase JWT Authentication System

Status: Accepted
Date: 2025-07-28
Decision makers: Engineering Team

Context

We've encountered recurring confusion about Supabase's authentication system. The platform has migrated from legacy API keys to a modern JWT-based security model, but our codebase and scripts still reference outdated authentication methods.

Current State Investigation

Based on our Supabase project and Edge Functions:

  1. Two Authentication Systems: Supabase supports both JWT tokens AND API keys
  2. API Key System:
    • Publishable Key (starts with sb_publishable_): Safe for client-side use
    • Secret Key (starts with sb_secret_): Server-side only, privileged access
  3. JWT System: User authentication tokens for authenticated requests
  4. Edge Functions: Use Deno 1.45.2 runtime with specific import requirements

Decision

We will use Supabase's dual authentication system with both API keys and JWT tokens:

1. Authentication Types

  • Client-side: Use publishable key (sbpublishable*) for public operations
  • Server-side/Edge Functions: Use secret key (sbsecret*) for privileged operations
  • User Authentication: Use JWT tokens from Supabase Auth sessions
  • Testing: Use secret key for server-side test operations

2. Environment Variables

# Modern Supabase environment variables
SUPABASE_URL=https://[project-ref].supabase.co
SUPABASE_PUBLISHABLE_KEY=sb_publishable_[key] # Client-safe operations
SUPABASE_SECRET_KEY=sb_secret_[key] # Server-side privileged access
# User JWT tokens are obtained dynamically through auth flow

3. Authentication Patterns

Client-side (React)

// Use Supabase Auth - no keys needed in client
const {
data: { user },
} = await supabase.auth.getUser()
const token = (await supabase.auth.getSession()).data.session?.access_token

Edge Functions

// Use secret key for privileged operations
const supabaseUrl = Deno.env.get('SUPABASE_URL')!
const supabaseSecretKey = Deno.env.get('SUPABASE_SECRET_KEY')!
const supabase = createClient(supabaseUrl, supabaseSecretKey)

// For user authentication from requests
const authHeader = req.headers.get('authorization')
const token = authHeader?.replace('Bearer ', '')
const {
data: { user },
} = await supabase.auth.getUser(token)

Testing Scripts

// For server-side testing, use secret key
const supabase = createClient(supabaseUrl, supabaseSecretKey)

// For user authentication testing
const { data, error } = await supabase.auth.signInWithOtp({
email: 'test@example.com',
})

4. Common Mistakes to Avoid

  • ❌ Looking for SUPABASE_ANON_KEY - use SUPABASE_PUBLISHABLE_KEY instead
  • ❌ Using publishable key for server-side operations - use secret key
  • ❌ Using secret key in client-side code - use publishable key
  • ❌ Hardcoding any keys in code
  • ❌ Using npm: imports in Edge Functions (use https://esm.sh/)

5. GraphQL Authentication

For our GraphQL endpoint, authentication should work as follows:

// No authentication required for health/metrics
if (url.pathname.endsWith('/health') || url.pathname.endsWith('/metrics')) {
// Public endpoints
}

// GraphQL queries require user authentication
const authHeader = req.headers.get('authorization')
if (!authHeader) {
return new Response('Unauthorized', { status: 401 })
}

Consequences

Positive

  • Enhanced security with JWT-based authentication
  • No client-side keys to manage or rotate
  • Proper user-based access control
  • Alignment with Supabase best practices

Negative

  • Breaking change for existing scripts expecting anon keys
  • Need to update all test scripts to use proper authentication
  • More complex testing setup (need actual user sessions)

Implementation Notes

  1. Update environment variables to use proper API key naming:
    • Replace VITE_SUPABASE_ANON_KEY with SUPABASE_PUBLISHABLE_KEY
    • Add SUPABASE_SECRET_KEY for server-side operations
  2. Update test scripts to use:
    • Secret key for server-side testing operations
    • User JWT tokens for user-specific tests
  3. Document the dual authentication system in README
  4. Update CI/CD to use correct API key environment variables

References