@@ -106,9 +106,9 @@ function getAuthToken(req: Request) {
106106 return token ;
107107}
108108
109- async function verifyLegacyJWT ( jwt : string ) : Promise < boolean > {
109+ async function isValidLegacyJWT ( jwtSecret : string , jwt : string ) : Promise < boolean > {
110110 const encoder = new TextEncoder ( ) ;
111- const secretKey = encoder . encode ( JWT_SECRET ) ;
111+ const secretKey = encoder . encode ( jwtSecret ) ;
112112 try {
113113 await jose . jwtVerify ( jwt , secretKey ) ;
114114 } catch ( e ) {
@@ -120,10 +120,10 @@ async function verifyLegacyJWT(jwt: string): Promise<boolean> {
120120
121121// Lazy-loading JWKs
122122let jwks = null ;
123- async function verifyJWT ( jwt : string ) : Promise < boolean > {
123+ async function isValidJWT ( jwksUrl : string , jwt : string ) : Promise < boolean > {
124124 try {
125125 if ( ! jwks ) {
126- jwks = jose . createRemoteJWKSet ( new URL ( JWKS_ENDPOINT ) ) ;
126+ jwks = jose . createRemoteJWKSet ( new URL ( jwksUrl ) ) ;
127127 }
128128 await jose . jwtVerify ( jwt , jwks ) ;
129129 } catch ( e ) {
@@ -133,6 +133,26 @@ async function verifyJWT(jwt: string): Promise<boolean> {
133133 return true ;
134134}
135135
136+ /**
137+ * Applies hybrid JWT verification, using JWK as primary and Legacy Secret as fallback.
138+ * Use only during 'New JWT Keys' migration period, while `JWT_SECRET` is still available.
139+ */
140+ export async function verifyHybridJWT ( jwtSecret : string , jwksUrl : string , jwt : string ) : Promise < boolean > {
141+ const { alg : jwtAlgorithm } = jose . decodeProtectedHeader ( jwt )
142+
143+ if ( jwtAlgorithm === 'HS256' ) {
144+ console . log ( `Legacy token type detected, attempting ${ jwtAlgorithm } verification.` )
145+
146+ return await isValidLegacyJWT ( jwtSecret , jwt )
147+ }
148+
149+ if ( jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256' ) {
150+ return await isValidJWT ( jwksUrl , jwt )
151+ }
152+
153+ return false ;
154+ }
155+
136156// Ref: https://docs.deno.com/examples/checking_file_existence/
137157async function shouldUsePackageJsonDiscovery ( { entrypointPath, importMapPath } : FunctionConfig ) : Promise < boolean > {
138158 if ( importMapPath ) {
@@ -175,15 +195,10 @@ Deno.serve({
175195 if ( req . method !== "OPTIONS" && functionsConfig [ functionName ] . verifyJWT ) {
176196 try {
177197 const token = getAuthToken ( req ) ;
178- const isValidJWT = await verifyJWT ( token ) ;
198+ const isValidJWT = await verifyHybridJWT ( JWT_SECRET , JWKS_ENDPOINT , token ) ;
179199
180200 if ( ! isValidJWT ) {
181- console . log ( 'Asymmetric JWT verification failed; attempting legacy verification.' )
182- const isValidLegacyJWT = await verifyLegacyJWT ( token ) ;
183-
184- if ( ! isValidLegacyJWT ) {
185- return getResponse ( { msg : "Invalid JWT" } , STATUS_CODE . Unauthorized ) ;
186- }
201+ return getResponse ( { msg : "Invalid JWT" } , STATUS_CODE . Unauthorized ) ;
187202 }
188203 } catch ( e ) {
189204 console . error ( e ) ;
0 commit comments