Buckets:
| import fetchJwks from '../runtime/fetch_jwks.js'; | |
| import { JWKSNoMatchingKey } from '../util/errors.js'; | |
| import { createLocalJWKSet } from './local.js'; | |
| import isObject from '../lib/is_object.js'; | |
| function isCloudflareWorkers() { | |
| return (typeof WebSocketPair !== 'undefined' || | |
| (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') || | |
| (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel')); | |
| } | |
| let USER_AGENT; | |
| if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) { | |
| const NAME = 'jose'; | |
| const VERSION = 'v5.9.6'; | |
| USER_AGENT = `${NAME}/${VERSION}`; | |
| } | |
| export const jwksCache = Symbol(); | |
| function isFreshJwksCache(input, cacheMaxAge) { | |
| if (typeof input !== 'object' || input === null) { | |
| return false; | |
| } | |
| if (!('uat' in input) || typeof input.uat !== 'number' || Date.now() - input.uat >= cacheMaxAge) { | |
| return false; | |
| } | |
| if (!('jwks' in input) || | |
| !isObject(input.jwks) || | |
| !Array.isArray(input.jwks.keys) || | |
| !Array.prototype.every.call(input.jwks.keys, isObject)) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| class RemoteJWKSet { | |
| constructor(url, options) { | |
| if (!(url instanceof URL)) { | |
| throw new TypeError('url must be an instance of URL'); | |
| } | |
| this._url = new URL(url.href); | |
| this._options = { agent: options?.agent, headers: options?.headers }; | |
| this._timeoutDuration = | |
| typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000; | |
| this._cooldownDuration = | |
| typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000; | |
| this._cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000; | |
| if (options?.[jwksCache] !== undefined) { | |
| this._cache = options?.[jwksCache]; | |
| if (isFreshJwksCache(options?.[jwksCache], this._cacheMaxAge)) { | |
| this._jwksTimestamp = this._cache.uat; | |
| this._local = createLocalJWKSet(this._cache.jwks); | |
| } | |
| } | |
| } | |
| coolingDown() { | |
| return typeof this._jwksTimestamp === 'number' | |
| ? Date.now() < this._jwksTimestamp + this._cooldownDuration | |
| : false; | |
| } | |
| fresh() { | |
| return typeof this._jwksTimestamp === 'number' | |
| ? Date.now() < this._jwksTimestamp + this._cacheMaxAge | |
| : false; | |
| } | |
| async getKey(protectedHeader, token) { | |
| if (!this._local || !this.fresh()) { | |
| await this.reload(); | |
| } | |
| try { | |
| return await this._local(protectedHeader, token); | |
| } | |
| catch (err) { | |
| if (err instanceof JWKSNoMatchingKey) { | |
| if (this.coolingDown() === false) { | |
| await this.reload(); | |
| return this._local(protectedHeader, token); | |
| } | |
| } | |
| throw err; | |
| } | |
| } | |
| async reload() { | |
| if (this._pendingFetch && isCloudflareWorkers()) { | |
| this._pendingFetch = undefined; | |
| } | |
| const headers = new Headers(this._options.headers); | |
| if (USER_AGENT && !headers.has('User-Agent')) { | |
| headers.set('User-Agent', USER_AGENT); | |
| this._options.headers = Object.fromEntries(headers.entries()); | |
| } | |
| this._pendingFetch || (this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options) | |
| .then((json) => { | |
| this._local = createLocalJWKSet(json); | |
| if (this._cache) { | |
| this._cache.uat = Date.now(); | |
| this._cache.jwks = json; | |
| } | |
| this._jwksTimestamp = Date.now(); | |
| this._pendingFetch = undefined; | |
| }) | |
| .catch((err) => { | |
| this._pendingFetch = undefined; | |
| throw err; | |
| })); | |
| await this._pendingFetch; | |
| } | |
| } | |
| export function createRemoteJWKSet(url, options) { | |
| const set = new RemoteJWKSet(url, options); | |
| const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token); | |
| Object.defineProperties(remoteJWKSet, { | |
| coolingDown: { | |
| get: () => set.coolingDown(), | |
| enumerable: true, | |
| configurable: false, | |
| }, | |
| fresh: { | |
| get: () => set.fresh(), | |
| enumerable: true, | |
| configurable: false, | |
| }, | |
| reload: { | |
| value: () => set.reload(), | |
| enumerable: true, | |
| configurable: false, | |
| writable: false, | |
| }, | |
| reloading: { | |
| get: () => !!set._pendingFetch, | |
| enumerable: true, | |
| configurable: false, | |
| }, | |
| jwks: { | |
| value: () => set._local?.jwks(), | |
| enumerable: true, | |
| configurable: false, | |
| writable: false, | |
| }, | |
| }); | |
| return remoteJWKSet; | |
| } | |
| export const experimental_jwksCache = jwksCache; | |
Xet Storage Details
- Size:
- 5.01 kB
- Xet hash:
- 377e84ca69b4e21121f4e38b065b60294af23578fbeb8098cac52a41de87904e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.