link
This commit is contained in:
21
node_modules/jwks-rsa/LICENSE
generated
vendored
Normal file
21
node_modules/jwks-rsa/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Sandrino Di Mattia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
83
node_modules/jwks-rsa/README.md
generated
vendored
Normal file
83
node_modules/jwks-rsa/README.md
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||

|
||||
|
||||

|
||||
[](https://codecov.io/gh/auth0/node-jwks-rsa)
|
||||

|
||||
[](https://opensource.org/licenses/MIT)
|
||||

|
||||
|
||||
📚 [Documentation](#documentation) - 🚀 [Getting Started](#getting-started) - 💬 [Feedback](#feedback)
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Examples](https://github.com/auth0/node-jwks-rsa/blob/master/EXAMPLES.md) - documentation of the options and code samples for common scenarios.
|
||||
- [Docs Site](https://auth0.com/docs) - explore our Docs site and learn more about Auth0.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Using [npm](https://npmjs.org) in your project directory run the following command:
|
||||
|
||||
````bash
|
||||
npm install --save jwks-rsa
|
||||
````
|
||||
|
||||
Supports all currently registered JWK types and JWS Algorithms, see [panva/jose#262](https://github.com/panva/jose/issues/262) for more information.
|
||||
|
||||
### Configure the client
|
||||
|
||||
Provide a JWKS endpoint which exposes your signing keys.
|
||||
|
||||
````js
|
||||
const jwksClient = require('jwks-rsa');
|
||||
|
||||
const client = jwksClient({
|
||||
jwksUri: 'https://sandrino.auth0.com/.well-known/jwks.json',
|
||||
requestHeaders: {}, // Optional
|
||||
timeout: 30000 // Defaults to 30s
|
||||
});
|
||||
````
|
||||
|
||||
### Retrieve a key
|
||||
|
||||
Then use `getSigningKey` to retrieve a signing key that matches a specific `kid`.
|
||||
|
||||
````js
|
||||
const kid = 'RkI5MjI5OUY5ODc1N0Q4QzM0OUYzNkVGMTJDOUEzQkFCOTU3NjE2Rg';
|
||||
const key = await client.getSigningKey(kid);
|
||||
const signingKey = key.getPublicKey();
|
||||
````
|
||||
|
||||
## Feedback
|
||||
|
||||
### Contributing
|
||||
|
||||
We appreciate feedback and contribution to this repo! Before you get started, please see the following:
|
||||
|
||||
- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
|
||||
- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
|
||||
|
||||
### Raise an issue
|
||||
|
||||
To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/node-jwks-rsa/issues).
|
||||
|
||||
### Vulnerability Reporting
|
||||
|
||||
Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues.
|
||||
|
||||
## What is Auth0?
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.auth0.com/website/sdks/logos/auth0_dark_mode.png" width="150">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://cdn.auth0.com/website/sdks/logos/auth0_light_mode.png" width="150">
|
||||
<img alt="Auth0 Logo" src="https://cdn.auth0.com/website/sdks/logos/auth0_light_mode.png" width="150">
|
||||
</picture>
|
||||
</p>
|
||||
<p align="center">
|
||||
Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout <a href="https://auth0.com/why-auth0">Why Auth0?</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
This project is licensed under the MIT license. See the <a href="https://github.com/auth0/node-jwks-rsa/blob/master/LICENSE"> LICENSE</a> file for more info.
|
||||
</p>
|
||||
130
node_modules/jwks-rsa/index.d.ts
generated
vendored
Normal file
130
node_modules/jwks-rsa/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Agent as HttpAgent } from 'http';
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import type {Jwt, Secret} from 'jsonwebtoken'
|
||||
import Express = require('express')
|
||||
|
||||
declare function JwksRsa(options: JwksRsa.Options): JwksRsa.JwksClient;
|
||||
|
||||
declare namespace JwksRsa {
|
||||
class JwksClient {
|
||||
constructor(options: Options);
|
||||
|
||||
getKeys(): Promise<unknown>;
|
||||
getSigningKeys(): Promise<SigningKey[]>;
|
||||
getSigningKey(kid?: string | null | undefined): Promise<SigningKey>;
|
||||
getSigningKey(kid: string | null | undefined, cb: (err: Error | null, key?: SigningKey) => void): void;
|
||||
}
|
||||
|
||||
interface Headers {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
jwksUri: string;
|
||||
rateLimit?: boolean;
|
||||
cache?: boolean;
|
||||
cacheMaxEntries?: number;
|
||||
cacheMaxAge?: number;
|
||||
jwksRequestsPerMinute?: number;
|
||||
proxy?: string;
|
||||
requestHeaders?: Headers;
|
||||
timeout?: number;
|
||||
requestAgent?: HttpAgent | HttpsAgent;
|
||||
fetcher?(jwksUri: string): Promise<{ keys: any }>;
|
||||
getKeysInterceptor?(): Promise<JSONWebKey[]>;
|
||||
}
|
||||
|
||||
interface JSONWebKey {
|
||||
kid: string,
|
||||
alg: string,
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface CertSigningKey {
|
||||
kid: string;
|
||||
alg: string;
|
||||
getPublicKey(): string;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
interface RsaSigningKey {
|
||||
kid: string;
|
||||
alg: string;
|
||||
getPublicKey(): string;
|
||||
rsaPublicKey: string;
|
||||
}
|
||||
|
||||
type SigningKey = CertSigningKey | RsaSigningKey;
|
||||
|
||||
/**
|
||||
* Types are duplicated from express-jwt@6/7
|
||||
* due to numerous breaking changes in the lib's types
|
||||
* whilst this lib supportd both <=6 & >=7 implementations
|
||||
*
|
||||
* express-jwt's installed version (or its @types)
|
||||
* will be the types used at transpilation time
|
||||
*/
|
||||
|
||||
/** Types from express-jwt@<=6 */
|
||||
type secretType = string|Buffer;
|
||||
type SecretCallbackLong = (req: unknown, header: any, payload: any, done: (err: any, secret?: secretType) => void) => void;
|
||||
type SecretCallback = (req: unknown, payload: any, done: (err: any, secret?: secretType) => void) => void;
|
||||
|
||||
/** Types from express-jwt@>=7 */
|
||||
type GetVerificationKey = (req: unknown, token: Jwt | undefined) => Secret | undefined | Promise<Secret | undefined>;
|
||||
|
||||
function expressJwtSecret(options: ExpressJwtOptions): SecretCallbackLong & GetVerificationKey;
|
||||
|
||||
function passportJwtSecret(options: ExpressJwtOptions): SecretCallback;
|
||||
|
||||
interface ExpressJwtOptions extends Options {
|
||||
handleSigningKeyError?: (err: Error | null, cb: (err: Error | null) => void) => void;
|
||||
}
|
||||
|
||||
function hapiJwt2Key(options: HapiJwtOptions): (decodedToken: DecodedToken, cb: HapiCallback) => void;
|
||||
|
||||
interface HapiJwtOptions extends Options {
|
||||
handleSigningKeyError?: (err: Error | null, cb: HapiCallback) => void;
|
||||
}
|
||||
|
||||
type HapiCallback = (err: Error | null, publicKey: string, signingKey: SigningKey) => void;
|
||||
|
||||
interface DecodedToken {
|
||||
header: TokenHeader;
|
||||
}
|
||||
|
||||
interface TokenHeader {
|
||||
alg: string;
|
||||
kid: string;
|
||||
}
|
||||
|
||||
function hapiJwt2KeyAsync(options: HapiJwtOptions): (decodedToken: DecodedToken) => Promise<{ key: string }>;
|
||||
|
||||
function koaJwtSecret(options: KoaJwtOptions): (header: TokenHeader) => Promise<string>;
|
||||
|
||||
interface KoaJwtOptions extends Options {
|
||||
handleSigningKeyError?(err: Error | null): Promise<void>;
|
||||
}
|
||||
|
||||
class ArgumentError extends Error {
|
||||
name: 'ArgumentError';
|
||||
constructor(message: string);
|
||||
}
|
||||
|
||||
class JwksError extends Error {
|
||||
name: 'JwksError';
|
||||
constructor(message: string);
|
||||
}
|
||||
|
||||
class JwksRateLimitError extends Error {
|
||||
name: 'JwksRateLimitError';
|
||||
constructor(message: string);
|
||||
}
|
||||
|
||||
class SigningKeyNotFoundError extends Error {
|
||||
name: 'SigningKeyNotFoundError';
|
||||
constructor(message: string);
|
||||
}
|
||||
}
|
||||
|
||||
export = JwksRsa;
|
||||
74
node_modules/jwks-rsa/package.json
generated
vendored
Normal file
74
node_modules/jwks-rsa/package.json
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "jwks-rsa",
|
||||
"version": "3.2.0",
|
||||
"description": "Library to retrieve RSA public keys from a JWKS endpoint",
|
||||
"main": "src/index.js",
|
||||
"files": [
|
||||
"src",
|
||||
"index.d.ts"
|
||||
],
|
||||
"types": "index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.20",
|
||||
"@types/jsonwebtoken": "^9.0.4",
|
||||
"debug": "^4.3.4",
|
||||
"jose": "^4.15.4",
|
||||
"limiter": "^1.1.5",
|
||||
"lru-memoizer": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.9",
|
||||
"@types/express-jwt-v6": "npm:@types/express-jwt@^6.0.4",
|
||||
"@types/mocha": "^10.0.3",
|
||||
"@types/nock": "^11.0.0",
|
||||
"@types/node": "^20.8.10",
|
||||
"chai": "^4.3.10",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"eslint": "^8.52.0",
|
||||
"express": "^4.18.2",
|
||||
"express-jwt": "^8.4.1",
|
||||
"express-jwt-v6": "npm:express-jwt@^6.1.2",
|
||||
"express-jwt-v7": "npm:express-jwt@^7.5.0",
|
||||
"jose2": "npm:jose@^2.0.6",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"koa": "^2.14.2",
|
||||
"koa-jwt": "^4.0.4",
|
||||
"mocha": "^10.2.0",
|
||||
"nock": "^13.3.7",
|
||||
"nyc": "^15.1.0",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"rimraf": "^5.0.5",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"clean:ts": "rimraf ts-output/",
|
||||
"lint": "eslint ./src ./tests",
|
||||
"test:ts": "npm run clean:ts && tsc && NODE_ENV=test mocha --exit --timeout 5000 $(find ./ts-output -name *.tests.js)",
|
||||
"test:js": "NODE_ENV=test mocha --exit --timeout 5000 $(find ./tests -name *.tests.js)",
|
||||
"test": "npm run test:js && npm run test:ts",
|
||||
"test:ci": "nyc --reporter=lcov npm test",
|
||||
"test-watch": "NODE_ENV=test mocha --exit --timeout 5000 $(find ./tests -name *.tests.js) --watch",
|
||||
"release": "git tag $npm_package_version && git push && git push --tags && npm publish"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/auth0/node-jwks-rsa.git"
|
||||
},
|
||||
"keywords": [
|
||||
"jwks",
|
||||
"rsa",
|
||||
"jwt"
|
||||
],
|
||||
"author": "Auth0",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/auth0/node-jwks-rsa/issues"
|
||||
},
|
||||
"homepage": "https://github.com/auth0/node-jwks-rsa#readme"
|
||||
}
|
||||
91
node_modules/jwks-rsa/src/JwksClient.js
generated
vendored
Normal file
91
node_modules/jwks-rsa/src/JwksClient.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
const logger = require('debug')('jwks');
|
||||
const { retrieveSigningKeys } = require('./utils') ;
|
||||
const { request, cacheSigningKey, rateLimitSigningKey, getKeysInterceptor, callbackSupport } = require('./wrappers');
|
||||
const JwksError = require('./errors/JwksError');
|
||||
const SigningKeyNotFoundError = require('./errors/SigningKeyNotFoundError');
|
||||
|
||||
class JwksClient {
|
||||
constructor(options) {
|
||||
this.options = {
|
||||
rateLimit: false,
|
||||
cache: true,
|
||||
timeout: 30000,
|
||||
...options
|
||||
};
|
||||
|
||||
// Initialize wrappers.
|
||||
if (this.options.getKeysInterceptor) {
|
||||
this.getSigningKey = getKeysInterceptor(this, options);
|
||||
}
|
||||
|
||||
if (this.options.rateLimit) {
|
||||
this.getSigningKey = rateLimitSigningKey(this, options);
|
||||
}
|
||||
if (this.options.cache) {
|
||||
this.getSigningKey = cacheSigningKey(this, options);
|
||||
}
|
||||
|
||||
this.getSigningKey = callbackSupport(this, options);
|
||||
}
|
||||
|
||||
async getKeys() {
|
||||
logger(`Fetching keys from '${this.options.jwksUri}'`);
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
uri: this.options.jwksUri,
|
||||
headers: this.options.requestHeaders,
|
||||
agent: this.options.requestAgent,
|
||||
timeout: this.options.timeout,
|
||||
fetcher: this.options.fetcher
|
||||
});
|
||||
|
||||
logger('Keys:', res.keys);
|
||||
return res.keys;
|
||||
} catch (err) {
|
||||
const { errorMsg } = err;
|
||||
logger('Failure:', errorMsg || err);
|
||||
throw (errorMsg ? new JwksError(errorMsg) : err);
|
||||
}
|
||||
}
|
||||
|
||||
async getSigningKeys() {
|
||||
const keys = await this.getKeys();
|
||||
|
||||
if (!keys || !keys.length) {
|
||||
throw new JwksError('The JWKS endpoint did not contain any keys');
|
||||
}
|
||||
|
||||
const signingKeys = await retrieveSigningKeys(keys);
|
||||
|
||||
if (!signingKeys.length) {
|
||||
throw new JwksError('The JWKS endpoint did not contain any signing keys');
|
||||
}
|
||||
|
||||
logger('Signing Keys:', signingKeys);
|
||||
return signingKeys;
|
||||
}
|
||||
|
||||
async getSigningKey (kid) {
|
||||
logger(`Fetching signing key for '${kid}'`);
|
||||
const keys = await this.getSigningKeys();
|
||||
|
||||
const kidDefined = kid !== undefined && kid !== null;
|
||||
if (!kidDefined && keys.length > 1) {
|
||||
logger('No KID specified and JWKS endpoint returned more than 1 key');
|
||||
throw new SigningKeyNotFoundError('No KID specified and JWKS endpoint returned more than 1 key');
|
||||
}
|
||||
|
||||
const key = keys.find(k => !kidDefined || k.kid === kid);
|
||||
if (key) {
|
||||
return key;
|
||||
} else {
|
||||
logger(`Unable to find a signing key that matches '${kid}'`);
|
||||
throw new SigningKeyNotFoundError(`Unable to find a signing key that matches '${kid}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
JwksClient
|
||||
};
|
||||
10
node_modules/jwks-rsa/src/errors/ArgumentError.js
generated
vendored
Normal file
10
node_modules/jwks-rsa/src/errors/ArgumentError.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
function ArgumentError(message) {
|
||||
Error.call(this, message);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = 'ArgumentError';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
ArgumentError.prototype = Object.create(Error.prototype);
|
||||
ArgumentError.prototype.constructor = ArgumentError;
|
||||
module.exports = ArgumentError;
|
||||
10
node_modules/jwks-rsa/src/errors/JwksError.js
generated
vendored
Normal file
10
node_modules/jwks-rsa/src/errors/JwksError.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
function JwksError(message) {
|
||||
Error.call(this, message);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = 'JwksError';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
JwksError.prototype = Object.create(Error.prototype);
|
||||
JwksError.prototype.constructor = JwksError;
|
||||
module.exports = JwksError;
|
||||
10
node_modules/jwks-rsa/src/errors/JwksRateLimitError.js
generated
vendored
Normal file
10
node_modules/jwks-rsa/src/errors/JwksRateLimitError.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
function JwksRateLimitError(message) {
|
||||
Error.call(this, message);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = 'JwksRateLimitError';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
JwksRateLimitError.prototype = Object.create(Error.prototype);
|
||||
JwksRateLimitError.prototype.constructor = JwksRateLimitError;
|
||||
module.exports = JwksRateLimitError;
|
||||
10
node_modules/jwks-rsa/src/errors/SigningKeyNotFoundError.js
generated
vendored
Normal file
10
node_modules/jwks-rsa/src/errors/SigningKeyNotFoundError.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
function SigningKeyNotFoundError(message) {
|
||||
Error.call(this, message);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = 'SigningKeyNotFoundError';
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
SigningKeyNotFoundError.prototype = Object.create(Error.prototype);
|
||||
SigningKeyNotFoundError.prototype.constructor = SigningKeyNotFoundError;
|
||||
module.exports = SigningKeyNotFoundError;
|
||||
6
node_modules/jwks-rsa/src/errors/index.js
generated
vendored
Normal file
6
node_modules/jwks-rsa/src/errors/index.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
ArgumentError: require('./ArgumentError'),
|
||||
JwksError: require('./JwksError'),
|
||||
JwksRateLimitError: require('./JwksRateLimitError'),
|
||||
SigningKeyNotFoundError: require('./SigningKeyNotFoundError')
|
||||
};
|
||||
22
node_modules/jwks-rsa/src/index.js
generated
vendored
Normal file
22
node_modules/jwks-rsa/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
const { JwksClient } = require('./JwksClient');
|
||||
const errors = require('./errors');
|
||||
const { hapiJwt2Key, hapiJwt2KeyAsync } = require('./integrations/hapi');
|
||||
const { expressJwtSecret } = require('./integrations/express');
|
||||
const { koaJwtSecret } = require('./integrations/koa');
|
||||
const { passportJwtSecret } = require('./integrations/passport');
|
||||
|
||||
module.exports = (options) => {
|
||||
return new JwksClient(options);
|
||||
};
|
||||
module.exports.JwksClient = JwksClient;
|
||||
|
||||
module.exports.ArgumentError = errors.ArgumentError;
|
||||
module.exports.JwksError = errors.JwksError;
|
||||
module.exports.JwksRateLimitError = errors.JwksRateLimitError;
|
||||
module.exports.SigningKeyNotFoundError = errors.SigningKeyNotFoundError;
|
||||
|
||||
module.exports.expressJwtSecret = expressJwtSecret;
|
||||
module.exports.hapiJwt2Key = hapiJwt2Key;
|
||||
module.exports.hapiJwt2KeyAsync = hapiJwt2KeyAsync;
|
||||
module.exports.koaJwtSecret = koaJwtSecret;
|
||||
module.exports.passportJwtSecret = passportJwtSecret;
|
||||
15
node_modules/jwks-rsa/src/integrations/config.js
generated
vendored
Normal file
15
node_modules/jwks-rsa/src/integrations/config.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
const allowedSignatureAlg = [
|
||||
'RS256',
|
||||
'RS384',
|
||||
'RS512',
|
||||
'PS256',
|
||||
'PS384',
|
||||
'PS512',
|
||||
'ES256',
|
||||
'ES256K',
|
||||
'ES384',
|
||||
'ES512',
|
||||
'EdDSA'
|
||||
];
|
||||
|
||||
module.exports = allowedSignatureAlg;
|
||||
60
node_modules/jwks-rsa/src/integrations/express.js
generated
vendored
Normal file
60
node_modules/jwks-rsa/src/integrations/express.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
const { ArgumentError } = require('../errors');
|
||||
const { JwksClient } = require('../JwksClient');
|
||||
const supportedAlg = require('./config');
|
||||
|
||||
const handleSigningKeyError = (err, cb) => {
|
||||
// If we didn't find a match, can't provide a key.
|
||||
if (err && err.name === 'SigningKeyNotFoundError') {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
// If an error occured like rate limiting or HTTP issue, we'll bubble up the error.
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.expressJwtSecret = function (options) {
|
||||
if (options === null || options === undefined) {
|
||||
throw new ArgumentError('An options object must be provided when initializing expressJwtSecret');
|
||||
}
|
||||
|
||||
const client = new JwksClient(options);
|
||||
const onError = options.handleSigningKeyError || handleSigningKeyError;
|
||||
|
||||
const expressJwt7Provider = async (req, token) => {
|
||||
if (!token) { return; }
|
||||
const header = token.header;
|
||||
if (!header || !supportedAlg.includes(header.alg)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const key = await client.getSigningKey(header.kid);
|
||||
return key.publicKey || key.rsaPublicKey;
|
||||
} catch (err) {
|
||||
return new Promise((resolve, reject) => {
|
||||
onError(err, (newError) => {
|
||||
if (!newError) { return resolve(); }
|
||||
reject(newError);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return function secretProvider(req, header, payload, cb) {
|
||||
//This function has 4 parameters to make it work with express-jwt@6
|
||||
//but it also supports express-jwt@7 which only has 2.
|
||||
if (arguments.length === 4) {
|
||||
expressJwt7Provider(req, { header })
|
||||
.then(key => {
|
||||
setImmediate(cb, null, key);
|
||||
}).catch(err => {
|
||||
setImmediate(cb, err);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return expressJwt7Provider(req, arguments[1]);
|
||||
};
|
||||
};
|
||||
59
node_modules/jwks-rsa/src/integrations/hapi.js
generated
vendored
Normal file
59
node_modules/jwks-rsa/src/integrations/hapi.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
const { ArgumentError } = require('../errors');
|
||||
const { JwksClient } = require('../JwksClient');
|
||||
const supportedAlg = require('./config');
|
||||
|
||||
const handleSigningKeyError = (err, cb) => {
|
||||
// If we didn't find a match, can't provide a key.
|
||||
if (err && err.name === 'SigningKeyNotFoundError') {
|
||||
return cb(err, null, null);
|
||||
}
|
||||
|
||||
// If an error occured like rate limiting or HTTP issue, we'll bubble up the error.
|
||||
if (err) {
|
||||
return cb(err, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call hapiJwt2Key as a Promise
|
||||
* @param {object} options
|
||||
* @returns {Promise}
|
||||
*/
|
||||
module.exports.hapiJwt2KeyAsync = (options) => {
|
||||
const secretProvider = module.exports.hapiJwt2Key(options);
|
||||
return function(decoded) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cb = (err, key) => {
|
||||
(!key || err) ? reject(err) : resolve({ key });
|
||||
};
|
||||
secretProvider(decoded, cb);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.hapiJwt2Key = function (options) {
|
||||
if (options === null || options === undefined) {
|
||||
throw new ArgumentError('An options object must be provided when initializing hapiJwt2Key');
|
||||
}
|
||||
|
||||
const client = new JwksClient(options);
|
||||
const onError = options.handleSigningKeyError || handleSigningKeyError;
|
||||
|
||||
return function secretProvider(decoded, cb) {
|
||||
// We cannot find a signing certificate if there is no header (no kid).
|
||||
if (!decoded || !decoded.header) {
|
||||
return cb(new Error('Cannot find a signing certificate if there is no header'), null, null);
|
||||
}
|
||||
|
||||
if (!supportedAlg.includes(decoded.header.alg)) {
|
||||
return cb(new Error('Unsupported algorithm ' + decoded.header.alg + ' supplied.'), null, null);
|
||||
}
|
||||
|
||||
client.getSigningKey(decoded.header.kid)
|
||||
.then(key => {
|
||||
return cb(null, key.publicKey || key.rsaPublicKey, key);
|
||||
}).catch(err => {
|
||||
return onError(err, (newError) => cb(newError, null, null));
|
||||
});
|
||||
};
|
||||
};
|
||||
30
node_modules/jwks-rsa/src/integrations/koa.js
generated
vendored
Normal file
30
node_modules/jwks-rsa/src/integrations/koa.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
const { ArgumentError } = require('../errors');
|
||||
const { JwksClient } = require('../JwksClient');
|
||||
const supportedAlg = require('./config');
|
||||
|
||||
module.exports.koaJwtSecret = function (options = {}) {
|
||||
if (!options.jwksUri) {
|
||||
throw new ArgumentError('No JWKS provided. Please provide a jwksUri');
|
||||
}
|
||||
|
||||
const client = new JwksClient(options);
|
||||
|
||||
return function secretProvider({ alg, kid } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!supportedAlg.includes(alg)) {
|
||||
return reject(new Error('Missing / invalid token algorithm'));
|
||||
}
|
||||
|
||||
client.getSigningKey(kid)
|
||||
.then(key => {
|
||||
resolve(key.publicKey || key.rsaPublicKey);
|
||||
}).catch(err => {
|
||||
if (options.handleSigningKeyError) {
|
||||
return options.handleSigningKeyError(err).then(reject);
|
||||
}
|
||||
|
||||
return reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
52
node_modules/jwks-rsa/src/integrations/passport.js
generated
vendored
Normal file
52
node_modules/jwks-rsa/src/integrations/passport.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
const jose = require('jose');
|
||||
const { ArgumentError } = require('../errors');
|
||||
const { JwksClient } = require('../JwksClient');
|
||||
const supportedAlg = require('./config');
|
||||
|
||||
const handleSigningKeyError = (err, cb) => {
|
||||
// If we didn't find a match, can't provide a key.
|
||||
if (err && err.name === 'SigningKeyNotFoundError') {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
// If an error occured like rate limiting or HTTP issue, we'll bubble up the error.
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.passportJwtSecret = function (options) {
|
||||
if (options === null || options === undefined) {
|
||||
throw new ArgumentError('An options object must be provided when initializing passportJwtSecret');
|
||||
}
|
||||
|
||||
if (!options.jwksUri) {
|
||||
throw new ArgumentError('No JWKS provided. Please provide a jwksUri');
|
||||
}
|
||||
|
||||
const client = new JwksClient(options);
|
||||
const onError = options.handleSigningKeyError || handleSigningKeyError;
|
||||
|
||||
return function secretProvider(req, rawJwtToken, cb) {
|
||||
let decoded;
|
||||
try {
|
||||
decoded = {
|
||||
payload: jose.decodeJwt(rawJwtToken),
|
||||
header: jose.decodeProtectedHeader(rawJwtToken)
|
||||
};
|
||||
} catch (err) {
|
||||
decoded = null;
|
||||
}
|
||||
|
||||
if (!decoded || !supportedAlg.includes(decoded.header.alg)) {
|
||||
return cb(null, null);
|
||||
}
|
||||
|
||||
client.getSigningKey(decoded.header.kid)
|
||||
.then(key => {
|
||||
cb(null, key.publicKey || key.rsaPublicKey);
|
||||
}).catch(err => {
|
||||
onError(err, (newError) => cb(newError, null));
|
||||
});
|
||||
};
|
||||
};
|
||||
80
node_modules/jwks-rsa/src/utils.js
generated
vendored
Normal file
80
node_modules/jwks-rsa/src/utils.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
const jose = require('jose');
|
||||
const JwksError = require('./errors/JwksError');
|
||||
|
||||
function resolveAlg(jwk) {
|
||||
if (jwk.alg) {
|
||||
return jwk.alg;
|
||||
}
|
||||
|
||||
if (jwk.kty === 'RSA') {
|
||||
return 'RS256';
|
||||
}
|
||||
|
||||
if (jwk.kty === 'EC') {
|
||||
switch (jwk.crv) {
|
||||
case 'P-256':
|
||||
return 'ES256';
|
||||
case 'secp256k1':
|
||||
return 'ES256K';
|
||||
case 'P-384':
|
||||
return 'ES384';
|
||||
case 'P-521':
|
||||
return 'ES512';
|
||||
}
|
||||
}
|
||||
|
||||
if (jwk.kty === 'OKP') {
|
||||
switch (jwk.crv) {
|
||||
case 'Ed25519':
|
||||
case 'Ed448':
|
||||
return 'EdDSA';
|
||||
}
|
||||
}
|
||||
|
||||
throw new JwksError('Unsupported JWK');
|
||||
}
|
||||
|
||||
async function retrieveSigningKeys(jwks) {
|
||||
const results = [];
|
||||
|
||||
jwks = jwks
|
||||
.filter(({ use }) => use === 'sig' || use === undefined)
|
||||
.filter(({ kty }) => kty === 'RSA' || kty === 'EC' || kty === 'OKP');
|
||||
|
||||
for (const jwk of jwks) {
|
||||
try {
|
||||
const key = await jose.importJWK({ ...jwk, ext: true }, resolveAlg(jwk));
|
||||
if (key.type !== 'public') {
|
||||
continue;
|
||||
}
|
||||
let getSpki;
|
||||
switch (key[Symbol.toStringTag]) {
|
||||
case 'CryptoKey': {
|
||||
const spki = await jose.exportSPKI(key);
|
||||
getSpki = () => spki;
|
||||
break;
|
||||
}
|
||||
case 'KeyObject':
|
||||
// Assume legacy Node.js version without the Symbol.toStringTag backported
|
||||
// Fall through
|
||||
default:
|
||||
getSpki = () => key.export({ format: 'pem', type: 'spki' });
|
||||
}
|
||||
results.push({
|
||||
get publicKey() { return getSpki(); },
|
||||
get rsaPublicKey() { return getSpki(); },
|
||||
getPublicKey() { return getSpki(); },
|
||||
...(typeof jwk.kid === 'string' && jwk.kid ? { kid: jwk.kid } : undefined),
|
||||
...(typeof jwk.alg === 'string' && jwk.alg ? { alg: jwk.alg } : undefined)
|
||||
});
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
retrieveSigningKeys
|
||||
};
|
||||
15
node_modules/jwks-rsa/src/wrappers/cache.js
generated
vendored
Normal file
15
node_modules/jwks-rsa/src/wrappers/cache.js
generated
vendored
Normal 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
node_modules/jwks-rsa/src/wrappers/callbackSupport.js
generated
vendored
Normal file
16
node_modules/jwks-rsa/src/wrappers/callbackSupport.js
generated
vendored
Normal 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
node_modules/jwks-rsa/src/wrappers/index.js
generated
vendored
Normal file
7
node_modules/jwks-rsa/src/wrappers/index.js
generated
vendored
Normal 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
node_modules/jwks-rsa/src/wrappers/interceptor.js
generated
vendored
Normal file
30
node_modules/jwks-rsa/src/wrappers/interceptor.js
generated
vendored
Normal 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 = await 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
node_modules/jwks-rsa/src/wrappers/rateLimit.js
generated
vendored
Normal file
34
node_modules/jwks-rsa/src/wrappers/rateLimit.js
generated
vendored
Normal 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
node_modules/jwks-rsa/src/wrappers/request.js
generated
vendored
Normal file
52
node_modules/jwks-rsa/src/wrappers/request.js
generated
vendored
Normal 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();
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user