fix(self-hosted): add hybrid JWT verification for edge functions docker template

This commit is contained in:
Atharv Gaur
2026-03-16 21:26:29 +05:30
committed by GitHub
parent 4d967740f7
commit edf51dfd98

View File

@@ -3,8 +3,31 @@ 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<typeof jose.createRemoteJWKSet> | 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)
}
}
/**
* 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
*/
function getAuthToken(req: Request) {
const authHeader = req.headers.get('authorization')
if (!authHeader) {
@@ -17,23 +40,75 @@ function getAuthToken(req: Request) {
return token
}
async function verifyJWT(jwt: string): Promise<boolean> {
const encoder = new TextEncoder()
const secretKey = encoder.encode(JWT_SECRET)
try {
await jose.jwtVerify(jwt, secretKey)
} catch (err) {
console.error(err)
async function isValidLegacyJWT(jwt: string): Promise<boolean> {
if (!JWT_SECRET) {
console.error('JWT_SECRET not available for HS256 token verification')
return false
}
return true
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<boolean> {
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;
}
/**
* Verify JWT token, handling both legacy (HS256) and newer (ES256/RS256) algorithms
*
* This function automatically detects the algorithm used in the token and applies
* the appropriate verification method:
* - HS256: Uses JWT_SECRET (symmetric key)
* - ES256/RS256: Uses JWKS endpoint (asymmetric public keys)
*
* This fix ensures compatibility with both legacy tokens and newer asymmetric tokens,
* resolving the "Key for the ES256 algorithm must be of type CryptoKey" error.
*
* @param jwt - The JWT token string to verify
* @returns Promise resolving to true if verification succeeds, false otherwise
*/
async function isValidHybridJWT(jwt: string): Promise<boolean> {
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 verifyJWT(token)
const isValidJWT = await isValidHybridJWT(token);
if (!isValidJWT) {
return new Response(JSON.stringify({ msg: 'Invalid JWT' }), {