import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' console.log('main function started') const JWT_SECRET = Deno.env.get('JWT_SECRET') const SUPABASE_URL = Deno.env.get('SUPABASE_URL') const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' // Create JWKS for ES256/RS256 tokens (newer tokens) let SUPABASE_JWT_KEYS: ReturnType | null = null if (SUPABASE_URL) { 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) } } function getAuthToken(req: Request) { const authHeader = req.headers.get('authorization') if (!authHeader) { throw new Error('Missing authorization header') } const [bearer, token] = authHeader.split(' ') if (bearer !== 'Bearer') { throw new Error(`Auth header is not 'Bearer {token}'`) } return token } async function isValidLegacyJWT(jwt: string): Promise { if (!JWT_SECRET) { console.error('JWT_SECRET not available for HS256 token verification') return false } const encoder = new TextEncoder(); const secretKey = encoder.encode(JWT_SECRET) try { await jose.jwtVerify(jwt, secretKey); } catch (e) { console.error('Symmetric Legacy JWT verification error', e); return false; } return true; } async function isValidJWT(jwt: string): Promise { if (!SUPABASE_JWT_KEYS) { console.error('JWKS not available for ES256/RS256 token verification') return false } try { await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS) } catch (e) { console.error('Asymmetric JWT verification error', e); return false } return true; } async function isValidHybridJWT(jwt: string): Promise { const { alg: jwtAlgorithm } = jose.decodeProtectedHeader(jwt) if (jwtAlgorithm === 'HS256') { console.log(`Legacy token type detected, attempting ${jwtAlgorithm} verification.`) return await isValidLegacyJWT(jwt) } if (jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256') { return await isValidJWT(jwt) } return false; } Deno.serve(async (req: Request) => { if (req.method !== 'OPTIONS' && VERIFY_JWT) { try { const token = getAuthToken(req) const isValidJWT = await isValidHybridJWT(token); if (!isValidJWT) { return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }) } } catch (e) { console.error(e) return new Response(JSON.stringify({ msg: e.toString() }), { status: 401, headers: { 'Content-Type': 'application/json' }, }) } } const url = new URL(req.url) const { pathname } = url const path_parts = pathname.split('/') const service_name = path_parts[1] if (!service_name || service_name === '') { const error = { msg: 'missing function name in request' } return new Response(JSON.stringify(error), { status: 400, headers: { 'Content-Type': 'application/json' }, }) } const servicePath = `/home/deno/functions/${service_name}` console.error(`serving the request with ${servicePath}`) const memoryLimitMb = 150 const workerTimeoutMs = 1 * 60 * 1000 const noModuleCache = false const importMapPath = null const envVarsObj = Deno.env.toObject() const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]) try { const worker = await EdgeRuntime.userWorkers.create({ servicePath, memoryLimitMb, workerTimeoutMs, noModuleCache, importMapPath, envVars, }) return await worker.fetch(req) } catch (e) { const error = { msg: e.toString() } return new Response(JSON.stringify(error), { status: 500, headers: { 'Content-Type': 'application/json' }, }) } })