@vzn/vx-devtools 0.0.648 → 0.0.650
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/oidc.ts +23 -1
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ export function oidcTokenAuth(
|
|
|
71
71
|
- jose checks the JWT: `RS256`, the signature against the issuer's keys
|
|
72
72
|
(found through its `/.well-known/openid-configuration`, kept and
|
|
73
73
|
refetched by `createRemoteJWKSet`), `aud`, `exp` and `nbf` within 60 s.
|
|
74
|
+
When a refetch fails, the keys last read still check tokens. A key they
|
|
75
|
+
lack is then `502`, not a refused token.
|
|
74
76
|
- With `oidcAuth`, every request names its repository: `vx-issuer` and `vx-repository`,
|
|
75
77
|
the `hashPart`s of the issuer's URL (no trailing slash) and of its
|
|
76
78
|
`org/name` lowercased; one missing or not a hash is `400`.
|
package/package.json
CHANGED
package/src/oidc.ts
CHANGED
|
@@ -293,6 +293,26 @@ function tokenVerifier(opts: OidcOptions): (token: string, req: Request) => Prom
|
|
|
293
293
|
const now = opts.now ?? (() => Date.now() / 1000)
|
|
294
294
|
// jose keeps each issuer's keys, and refetches them for a key it lacks.
|
|
295
295
|
const keys = new Map<string, Promise<ReturnType<typeof createRemoteJWKSet>>>()
|
|
296
|
+
// The key set each URL last answered. jose refetches every 10 minutes and
|
|
297
|
+
// threw when that failed: one slow answer from GitHub's key endpoint failed
|
|
298
|
+
// an upload 502 and lost a DTE worker's entry (2026-10-29). Keys rotate
|
|
299
|
+
// rarely, so the last set answers until the issuer answers again.
|
|
300
|
+
const lastRead = new Map<string, unknown>()
|
|
301
|
+
// The issuers answered from lastRead since their last fetch: a kid that
|
|
302
|
+
// set lacks is then the issuer out of reach, not a token refused.
|
|
303
|
+
const stale = new Set<string>()
|
|
304
|
+
const keySet = async (iss: string, url: string): Promise<unknown> => {
|
|
305
|
+
try {
|
|
306
|
+
const json = await fetchJson(url)
|
|
307
|
+
lastRead.set(url, json)
|
|
308
|
+
stale.delete(iss)
|
|
309
|
+
return json
|
|
310
|
+
} catch (err) {
|
|
311
|
+
if (!lastRead.has(url)) throw err
|
|
312
|
+
stale.add(iss)
|
|
313
|
+
return lastRead.get(url)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
296
316
|
const keysOf = (iss: string) => {
|
|
297
317
|
let set = keys.get(iss)
|
|
298
318
|
if (set === undefined) {
|
|
@@ -300,7 +320,7 @@ function tokenVerifier(opts: OidcOptions): (token: string, req: Request) => Prom
|
|
|
300
320
|
const uri = (c as { jwks_uri?: unknown }).jwks_uri
|
|
301
321
|
if (typeof uri !== 'string') throw new Error('no jwks_uri')
|
|
302
322
|
return createRemoteJWKSet(new URL(uri), {
|
|
303
|
-
[customFetch]: async (url: string) => Response.json(await
|
|
323
|
+
[customFetch]: async (url: string) => Response.json(await keySet(iss, url)),
|
|
304
324
|
})
|
|
305
325
|
})
|
|
306
326
|
keys.set(iss, set)
|
|
@@ -346,6 +366,8 @@ function tokenVerifier(opts: OidcOptions): (token: string, req: Request) => Prom
|
|
|
346
366
|
})
|
|
347
367
|
).payload
|
|
348
368
|
} catch (err) {
|
|
369
|
+
if (err instanceof errors.JWKSNoMatchingKey && stale.has(iss))
|
|
370
|
+
throw keysUnread(iss, new Error('a key the keys last read lack, and a refetch failed'))
|
|
349
371
|
throw refusal(err, iss, audience)
|
|
350
372
|
}
|
|
351
373
|
const claim = repoClaimOf(iss)
|