Skip to content

Commit 00c30af

Browse files
committed
stamp: detect algorithm before verify JWT
It helps to reduce latency for Legacy token verifications, since it avoid unnecessary requests.
1 parent 39176e3 commit 00c30af

File tree

1 file changed

+26
-11
lines changed
  • internal/functions/serve/templates

1 file changed

+26
-11
lines changed

internal/functions/serve/templates/main.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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
122122
let 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/
137157
async 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

Comments
 (0)