Skip to content

Commit 4301c6b

Browse files
Address code review feedback: single-flight JWK cache and add future-maintainer comment
Agent-Logs-Url: https://github.com/geturbackend/urBackend/sessions/b448bc8a-7b88-4981-bd67-1b8225e6973c Co-authored-by: yash-pouranik <172860064+yash-pouranik@users.noreply.github.com>
1 parent 72ea54b commit 4301c6b

2 files changed

Lines changed: 32 additions & 15 deletions

File tree

‎apps/dashboard-api/src/controllers/project.controller.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,8 @@ module.exports.toggleAuth = async (req, res) => {
13451345

13461346
// Ensure user owns project, and load authProviders secrets so sanitizeAuthProviders
13471347
// can correctly compute hasClientSecret in the response.
1348+
// NOTE: If new OAuth providers are added to SOCIAL_PROVIDER_KEYS, extend this select list
1349+
// to include their clientSecret fields as well.
13481350
const project = await Project.findOne({
13491351
_id: projectId,
13501352
owner: req.user._id,

‎apps/public-api/src/controllers/userAuth.controller.js‎

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,13 @@ const toBase64UrlBuffer = (input) => Buffer.from(input.replace(/-/g, '+').replac
7575
/**
7676
* In-memory cache for Google's public JWK keys.
7777
* Keys are refreshed when the cache expires (based on Cache-Control max-age).
78+
* An in-flight promise is stored to prevent redundant concurrent fetches (single-flight).
7879
*/
79-
const googleJwkCache = { keys: null, expiresAt: 0 };
80+
const googleJwkCache = { keys: null, expiresAt: 0, inflight: null };
8081

8182
/**
8283
* Fetches Google's public JWK keys, using an in-memory cache keyed by Cache-Control max-age.
84+
* Uses a single-flight pattern so that concurrent requests share one fetch instead of many.
8385
* @returns {Promise<Array>} Array of JWK key objects
8486
*/
8587
const getGooglePublicKeys = async () => {
@@ -88,25 +90,38 @@ const getGooglePublicKeys = async () => {
8890
return googleJwkCache.keys;
8991
}
9092

91-
const certsResponse = await fetch('https://www.googleapis.com/oauth2/v3/certs');
92-
if (!certsResponse.ok) {
93-
throw new Error('Unable to fetch Google JWK keys');
93+
// Single-flight: reuse in-flight promise if a fetch is already in progress.
94+
if (googleJwkCache.inflight) {
95+
return googleJwkCache.inflight;
9496
}
9597

96-
const certsPayload = await certsResponse.json();
97-
const keys = certsPayload.keys || [];
98+
googleJwkCache.inflight = (async () => {
99+
try {
100+
const certsResponse = await fetch('https://www.googleapis.com/oauth2/v3/certs');
101+
if (!certsResponse.ok) {
102+
throw new Error('Unable to fetch Google JWK keys');
103+
}
104+
105+
const certsPayload = await certsResponse.json();
106+
const keys = certsPayload.keys || [];
98107

99-
// Parse Cache-Control max-age from response headers to determine TTL.
100-
const cacheControl = (typeof certsResponse.headers?.get === 'function')
101-
? (certsResponse.headers.get('cache-control') || '')
102-
: '';
103-
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
104-
const ttlMs = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) * 1000 : 3600 * 1000;
108+
// Parse Cache-Control max-age from response headers to determine TTL.
109+
const cacheControl = (typeof certsResponse.headers?.get === 'function')
110+
? (certsResponse.headers.get('cache-control') || '')
111+
: '';
112+
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
113+
const ttlMs = maxAgeMatch ? parseInt(maxAgeMatch[1], 10) * 1000 : 3600 * 1000;
105114

106-
googleJwkCache.keys = keys;
107-
googleJwkCache.expiresAt = now + ttlMs;
115+
googleJwkCache.keys = keys;
116+
googleJwkCache.expiresAt = Date.now() + ttlMs;
117+
118+
return keys;
119+
} finally {
120+
googleJwkCache.inflight = null;
121+
}
122+
})();
108123

109-
return keys;
124+
return googleJwkCache.inflight;
110125
};
111126

112127
/**

0 commit comments

Comments
 (0)