NeahNew/node_modules/openid-client/lib/helpers/issuer.js
2025-05-03 14:17:46 +02:00

112 lines
2.7 KiB
JavaScript

const objectHash = require('object-hash');
const LRU = require('lru-cache');
const { RPError } = require('../errors');
const { assertIssuerConfiguration } = require('./assert');
const KeyStore = require('./keystore');
const { keystores } = require('./weak_cache');
const processResponse = require('./process_response');
const request = require('./request');
const inFlight = new WeakMap();
const caches = new WeakMap();
const lrus = (ctx) => {
if (!caches.has(ctx)) {
caches.set(ctx, new LRU({ max: 100 }));
}
return caches.get(ctx);
};
async function getKeyStore(reload = false) {
assertIssuerConfiguration(this, 'jwks_uri');
const keystore = keystores.get(this);
const cache = lrus(this);
if (reload || !keystore) {
if (inFlight.has(this)) {
return inFlight.get(this);
}
cache.reset();
inFlight.set(
this,
(async () => {
const response = await request
.call(this, {
method: 'GET',
responseType: 'json',
url: this.jwks_uri,
headers: {
Accept: 'application/json, application/jwk-set+json',
},
})
.finally(() => {
inFlight.delete(this);
});
const jwks = processResponse(response);
const joseKeyStore = KeyStore.fromJWKS(jwks, { onlyPublic: true });
cache.set('throttle', true, 60 * 1000);
keystores.set(this, joseKeyStore);
return joseKeyStore;
})(),
);
return inFlight.get(this);
}
return keystore;
}
async function queryKeyStore({ kid, kty, alg, use }, { allowMulti = false } = {}) {
const cache = lrus(this);
const def = {
kid,
kty,
alg,
use,
};
const defHash = objectHash(def, {
algorithm: 'sha256',
ignoreUnknown: true,
unorderedArrays: true,
unorderedSets: true,
respectType: false,
});
// refresh keystore on every unknown key but also only upto once every minute
const freshJwksUri = cache.get(defHash) || cache.get('throttle');
const keystore = await getKeyStore.call(this, !freshJwksUri);
const keys = keystore.all(def);
delete def.use;
if (keys.length === 0) {
throw new RPError({
printf: ["no valid key found in issuer's jwks_uri for key parameters %j", def],
jwks: keystore,
});
}
if (!allowMulti && keys.length > 1 && !kid) {
throw new RPError({
printf: [
"multiple matching keys found in issuer's jwks_uri for key parameters %j, kid must be provided in this case",
def,
],
jwks: keystore,
});
}
cache.set(defHash, true);
return keys;
}
module.exports.queryKeyStore = queryKeyStore;
module.exports.keystore = getKeyStore;