feat(self-host): functions expose jwks (#45635)

This commit is contained in:
Kalleby Santos authored and GitHub committed 2026-07-08 15:32:12 +02:00
1 parent c84d9856ae
commit 9cf6ae1f67
3 files changed
+27 -16

No files matched your search

+4
View File
@@ -456,6 +456,10 @@ services:
environment:
# Legacy symmetric HS256 key
JWT_SECRET: ${JWT_SECRET}
# JWKS for token verification (EC public + legacy symmetric).
# For Podman, use: SUPABASE_JWKS: ${JWT_JWKS}
#SUPABASE_JWKS: ${JWT_JWKS:-{"keys":[]}}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
# Legacy API keys (HS256-signed JWTs)
+4 -1
View File
@@ -168,6 +168,7 @@ echo ""
echo " Auth: GOTRUE_JWT_KEYS: \${JWT_KEYS:-[]}"
echo " Realtime: API_JWT_JWKS: \${JWT_JWKS:-{\"keys\":[]}}"
echo " Storage: JWT_JWKS: \${JWT_JWKS:-{\"keys\":[]}}"
echo " Functions: SUPABASE_JWKS: \${JWT_JWKS:-{\"keys\":[]}}"
echo ""
if [ "$1" = "--update-env" ]; then
@@ -212,11 +213,13 @@ sed -i.old \
-e '/^[ ]*#GOTRUE_JWT_KEYS:/ s/#//' \
-e '/^[ ]*#API_JWT_JWKS:/ s/#//' \
-e '/^[ ]*#JWT_JWKS:/ s/#//' \
-e '/^[ ]*#SUPABASE_JWKS:/ s/#//' \
docker-compose.yml || true
if grep -q '^[ ]*GOTRUE_JWT_KEYS:' docker-compose.yml && \
grep -q '^[ ]*API_JWT_JWKS:' docker-compose.yml && \
grep -q '^[ ]*JWT_JWKS:' docker-compose.yml; then
grep -q '^[ ]*JWT_JWKS:' docker-compose.yml && \
grep -q '^[ ]*SUPABASE_JWKS:' docker-compose.yml; then
echo "Done."
else
echo "Warning: could not edit docker-compose.yml. Uncomment auth configuration manually."
+19 -15
View File
@@ -1,29 +1,32 @@
import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts'
import * as jose from 'jsr:@panva/jose@6'
console.log('main function started')
const JWT_SECRET = Deno.env.get('JWT_SECRET')
const SUPABASE_URL = Deno.env.get('SUPABASE_URL')
const SUPABASE_JWKS = parseJwks(Deno.env.get('SUPABASE_JWKS'))
const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true'
// Create JWKS for ES256/RS256 tokens (newer tokens)
let SUPABASE_JWT_KEYS: ReturnType<typeof jose.createRemoteJWKSet> | null = null
if (SUPABASE_URL) {
// NOTE:(kallebysantos) We don't check for valid keys but just the bare array parsing,
// let this for 'jose' lib verification
export function parseJwks(raw: string | undefined): jose.JSONWebKeySet | null {
if (!raw) return null
try {
SUPABASE_JWT_KEYS = jose.createRemoteJWKSet(
new URL('/auth/v1/.well-known/jwks.json', SUPABASE_URL)
)
} catch (e) {
console.error('Failed to fetch JWKS from SUPABASE_URL:', e)
const parsed = JSON.parse(raw)
if (parsed?.keys && Array.isArray(parsed.keys)) {
return parsed as jose.JSONWebKeySet
}
return null
} catch {
return null
}
}
/**
* Extract JWT token from Authorization header
*
*
* Parses the Authorization header to extract the Bearer token.
* Expects format: "Bearer <token>"
*
*
* @param req - The HTTP request object
* @returns The JWT token string
* @throws Error if Authorization header is missing or malformed
@@ -47,7 +50,7 @@ async function isValidLegacyJWT(jwt: string): Promise<boolean> {
}
const encoder = new TextEncoder();
const secretKey = encoder.encode(JWT_SECRET)
const secretKey = encoder.encode(JWT_SECRET);
try {
await jose.jwtVerify(jwt, secretKey);
@@ -59,13 +62,14 @@ async function isValidLegacyJWT(jwt: string): Promise<boolean> {
}
async function isValidJWT(jwt: string): Promise<boolean> {
if (!SUPABASE_JWT_KEYS) {
if (!SUPABASE_JWKS) {
console.error('JWKS not available for ES256/RS256 token verification')
return false
}
try {
await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS)
const localJwks = jose.createLocalJWKSet(SUPABASE_JWKS);
await jose.jwtVerify(jwt, localJwks);
} catch (e) {
console.error('Asymmetric JWT verification error', e);
return false