Initial commit

This commit is contained in:
talksik
2021-12-29 01:57:42 -08:00
commit ce39a60b42
4634 changed files with 997667 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
const logger = require('debug')('jwks');
const memoizer = require('lru-memoizer');
const { promisify, callbackify } = require('util');
function cacheWrapper(client, { cacheMaxEntries = 5, cacheMaxAge = 600000 }) {
logger(`Configured caching of signing keys. Max: ${cacheMaxEntries} / Age: ${cacheMaxAge}`);
return promisify(memoizer({
hash: (kid) => kid,
load: callbackify(client.getSigningKey.bind(client)),
maxAge: cacheMaxAge,
max: cacheMaxEntries
}));
}
module.exports.default = cacheWrapper;
+16
View File
@@ -0,0 +1,16 @@
const { callbackify } = require('util');
const callbackSupport = (client) => {
const getSigningKey = client.getSigningKey.bind(client);
return (kid, cb) => {
if (cb) {
const callbackFunc = callbackify(getSigningKey);
return callbackFunc(kid, cb);
}
return getSigningKey(kid);
};
};
module.exports.default = callbackSupport;
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
request: require('./request').default,
cacheSigningKey: require('./cache').default,
rateLimitSigningKey: require('./rateLimit').default,
getKeysInterceptor: require('./interceptor').default,
callbackSupport: require('./callbackSupport').default
};
+30
View File
@@ -0,0 +1,30 @@
const retrieveSigningKeys = require('../utils').retrieveSigningKeys;
/**
* Uses getKeysInterceptor to allow users to retrieve keys from a file,
* external cache, or provided object before falling back to the jwksUri endpoint
*/
function getKeysInterceptor(client, { getKeysInterceptor }) {
const getSigningKey = client.getSigningKey.bind(client);
return async (kid) => {
const keys = await getKeysInterceptor();
let signingKeys;
if (keys && keys.length) {
signingKeys = retrieveSigningKeys(keys);
}
if (signingKeys && signingKeys.length) {
const key = signingKeys.find(k => !kid || k.kid === kid);
if (key) {
return key;
}
}
return getSigningKey(kid);
};
}
module.exports.default = getKeysInterceptor;
+34
View File
@@ -0,0 +1,34 @@
const logger = require('debug')('jwks');
const { RateLimiter } = require('limiter');
const JwksRateLimitError = require('../errors/JwksRateLimitError');
function rateLimitWrapper(client, { jwksRequestsPerMinute = 10 }) {
const getSigningKey = client.getSigningKey.bind(client);
const limiter = new RateLimiter(jwksRequestsPerMinute, 'minute', true);
logger(`Configured rate limiting to JWKS endpoint at ${jwksRequestsPerMinute}/minute`);
return async (kid) => await new Promise((resolve, reject) => {
limiter.removeTokens(1, async (err, remaining) => {
if (err) {
reject(err);
}
logger('Requests to the JWKS endpoint available for the next minute:', remaining);
if (remaining < 0) {
logger('Too many requests to the JWKS endpoint');
reject(new JwksRateLimitError('Too many requests to the JWKS endpoint'));
} else {
try {
const key = await getSigningKey(kid);
resolve(key);
} catch (error) {
reject(error);
}
}
});
});
}
module.exports.default = rateLimitWrapper;
+52
View File
@@ -0,0 +1,52 @@
const http = require('http');
const https = require('https');
const urlUtil = require('url');
module.exports.default = (options) => {
if (options.fetcher) {
return options.fetcher(options.uri);
}
return new Promise((resolve, reject) => {
const {
hostname,
path,
port,
protocol
} = urlUtil.parse(options.uri);
const requestOptions = {
hostname,
path,
port,
method: 'GET',
...(options.headers && { headers: { ...options.headers } }),
...(options.timeout && { timeout: options.timeout }),
...(options.agent && { agent: options.agent })
};
const httpRequestLib = protocol === 'https:' ? https : http;
const httpRequest = httpRequestLib.request(requestOptions, (res) => {
let rawData = '';
res.setEncoding('utf8');
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
if (res.statusCode < 200 || res.statusCode >= 300) {
const errorMsg = res.body && (res.body.message || res.body) || res.statusMessage || `Http Error ${res.statusCode}`;
reject({ errorMsg });
} else {
try {
resolve(rawData && JSON.parse(rawData));
} catch (error) {
reject(error);
}
}
});
});
httpRequest
.on('timeout', () => httpRequest.destroy())
.on('error', (e) => reject(e))
.end();
});
};