mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat(self-host): functions expose jwks (#45635)
This commit is contained in:
1 parent
c84d9856ae
commit
9cf6ae1f67
3 files changed
+27
-16
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user