From 0e4a217608067ef19800b877e13f1c6c1a6861b6 Mon Sep 17 00:00:00 2001 From: kallebysantos Date: Wed, 14 Jan 2026 11:33:56 +0000 Subject: [PATCH] feat: adding hybrid jwt verification Allows verify new JWTs as well legacy --- internal/functions/serve/templates/main.ts | 31 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/internal/functions/serve/templates/main.ts b/internal/functions/serve/templates/main.ts index f6676c163..997ed1089 100644 --- a/internal/functions/serve/templates/main.ts +++ b/internal/functions/serve/templates/main.ts @@ -1,7 +1,7 @@ import { STATUS_CODE, STATUS_TEXT } from "https://deno.land/std/http/status.ts"; import * as posix from "https://deno.land/std/path/posix/mod.ts"; -import * as jose from "https://deno.land/x/jose@v4.13.1/index.ts"; +import * as jose from "jsr:@panva/jose@6"; const SB_SPECIFIC_ERROR_CODE = { BootError: @@ -29,8 +29,9 @@ const SB_SPECIFIC_ERROR_REASON = { // OS stuff - we don't want to expose these to the functions. const EXCLUDED_ENVS = ["HOME", "HOSTNAME", "PATH", "PWD"]; -const JWT_SECRET = Deno.env.get("SUPABASE_INTERNAL_JWT_SECRET")!; const HOST_PORT = Deno.env.get("SUPABASE_INTERNAL_HOST_PORT")!; +const JWT_SECRET = Deno.env.get("SUPABASE_INTERNAL_JWT_SECRET")!; +const JWKS_ENDPOINT = Deno.env.get("SUPABASE_URL")! + "/auth/v1/.well-known/jwks.json"; const DEBUG = Deno.env.get("SUPABASE_INTERNAL_DEBUG") === "true"; const FUNCTIONS_CONFIG_STRING = Deno.env.get( "SUPABASE_INTERNAL_FUNCTIONS_CONFIG", @@ -104,13 +105,28 @@ function getAuthToken(req: Request) { return token; } -async function verifyJWT(jwt: string): Promise { +async function verifyLegacyJWT(jwt: string): Promise { const encoder = new TextEncoder(); const secretKey = encoder.encode(JWT_SECRET); try { await jose.jwtVerify(jwt, secretKey); } catch (e) { - console.error(e); + console.error('Symmetric Legacy JWT verification error', e); + return false; + } + return true; +} + +// Lazy-loading JWKs +let jwks = null; +async function verifyJWT(jwt: string): Promise { + try { + if (!jwks) { + jwks = jose.createRemoteJWKSet(new URL(JWKS_ENDPOINT)); + } + await jose.jwtVerify(jwt, jwks); + } catch (e) { + console.error('Asymmetric JWT verification error', e); return false; } return true; @@ -161,7 +177,12 @@ Deno.serve({ const isValidJWT = await verifyJWT(token); if (!isValidJWT) { - return getResponse({ msg: "Invalid JWT" }, STATUS_CODE.Unauthorized); + console.log('Asymmetric JWT verification failed; attempting legacy verification.') + const isValidLegacyJWT = await verifyLegacyJWT(token); + + if (!isValidLegacyJWT) { + return getResponse({ msg: "Invalid JWT" }, STATUS_CODE.Unauthorized); + } } } catch (e) { console.error(e);