diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02d932cfa..90d9393fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,16 +43,10 @@ jobs: strategy: matrix: node-version: - - 10.13.0 - - 10 - - 12.0.0 + - 12.19.0 - 12 - - 13.7.0 - - 13 - - 14.0.0 + - 14.15.0 - 14 - - 15.0.1 - - 15 os: - ubuntu-latest - windows-latest diff --git a/certification/runner/api.js b/certification/runner/api.js index 3487eb478..ab45422de 100644 --- a/certification/runner/api.js +++ b/certification/runner/api.js @@ -2,7 +2,7 @@ const { strict: assert } = require('assert'); const { createWriteStream } = require('fs'); -const Got = require('got'); +const Got = require('got'); // TODO: upgrade got const ms = require('ms'); const debug = require('./debug'); diff --git a/docs/README.md b/docs/README.md index a7c87c36f..3efc5f01e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1170,7 +1170,7 @@ _**default value**_: [RFC 8705](https://tools.ietf.org/html/rfc8705) - OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens -Enables specific features from the Mutual TLS specification. The three main features have their own specific setting in this feature's configuration object and you must provide functions for resolving some of the functions which are deployment-specific. Note: **This feature is only supported in node runtime >= 12.0.0** +Enables specific features from the Mutual TLS specification. The three main features have their own specific setting in this feature's configuration object and you must provide functions for resolving some of the functions which are deployment-specific. @@ -3218,7 +3218,7 @@ _**default value**_: ```js [ - // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + // asymmetric RSAES based 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', // asymmetric ECDH-ES based 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -3282,7 +3282,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3310,7 +3310,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3337,7 +3337,7 @@ _**default value**_: ```js [ - // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + // asymmetric RSAES based 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', // asymmetric ECDH-ES based 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -3402,7 +3402,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3429,7 +3429,7 @@ _**default value**_: ```js [ - // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + // asymmetric RSAES based 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', // asymmetric ECDH-ES based 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -3493,7 +3493,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3524,7 +3524,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3551,7 +3551,7 @@ _**default value**_: ```js [ - // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + // asymmetric RSAES based 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', // asymmetric ECDH-ES based 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -3616,7 +3616,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3646,7 +3646,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3676,7 +3676,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` @@ -3703,7 +3703,7 @@ _**default value**_: ```js [ - // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + // asymmetric RSAES based 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', // asymmetric ECDH-ES based 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -3768,7 +3768,7 @@ _**default value**_: 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + 'EdDSA', ] ``` diff --git a/lib/actions/introspection.js b/lib/actions/introspection.js index f7b1e6143..83325e1ae 100644 --- a/lib/actions/introspection.js +++ b/lib/actions/introspection.js @@ -194,7 +194,7 @@ module.exports = function introspectionAction(provider) { jti: token.jti, // TODO: in v7.x omit if jti === params.token aud: token.aud, scope: token.scope, - cnf: token.isSenderConstrained() ? {} : undefined, // TODO: in v7.x omit if RefreshToken + cnf: token.isSenderConstrained() ? {} : undefined, token_type: token.kind !== 'RefreshToken' ? token.tokenType : undefined, }); diff --git a/lib/adapters/memory_adapter.js b/lib/adapters/memory_adapter.js index ca5f1d167..3861c93cf 100644 --- a/lib/adapters/memory_adapter.js +++ b/lib/adapters/memory_adapter.js @@ -1,8 +1,8 @@ -const LRU = require('lru-cache'); +const Cache = require('lru-cache'); const epochTime = require('../helpers/epoch_time'); -let storage = new LRU({}); +let storage = new Cache(); function grantKeyFor(id) { return `grant:${id}`; diff --git a/lib/consts/jwa.js b/lib/consts/jwa.js index f060269a4..7176e4855 100644 --- a/lib/consts/jwa.js +++ b/lib/consts/jwa.js @@ -1,17 +1,15 @@ -const runtimeSupport = require('../helpers/runtime_support'); - const signingAlgValues = [ 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES256K', 'ES384', 'ES512', - runtimeSupport.EdDSA ? 'EdDSA' : undefined, -].filter(Boolean); + 'EdDSA', +]; const encryptionAlgValues = [ // asymmetric 'RSA-OAEP', - ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), + 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', // symmetric @@ -19,7 +17,7 @@ const encryptionAlgValues = [ 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', // direct 'dir', -].filter(Boolean); +]; const encryptionEncValues = [ 'A128CBC-HS256', 'A128GCM', 'A192CBC-HS384', 'A192GCM', 'A256CBC-HS512', 'A256GCM', diff --git a/lib/helpers/configuration.js b/lib/helpers/configuration.js index 7544b512a..4e0ba4845 100644 --- a/lib/helpers/configuration.js +++ b/lib/helpers/configuration.js @@ -13,7 +13,6 @@ const docs = require('./docs'); const getDefaults = require('./defaults'); const { STABLE, DRAFTS } = require('./features'); const attention = require('./attention'); -const runtimeSupport = require('./runtime_support'); const instance = require('./weak_cache'); function authEndpointDefaults(config) { @@ -73,8 +72,6 @@ class Configuration { this.logDraftNotice(); - this.checkRuntimeFeatures(); - this.ensureMaps(); this.ensureSets(); @@ -149,15 +146,6 @@ class Configuration { }); } - checkRuntimeFeatures() { - if (this.features.mTLS.enabled && !runtimeSupport.KeyObject) { - throw new TypeError('mTLS can only be enabled on Node.js >= 12.0.0 runtime'); - } - if ((this.formats.AccessToken === 'paseto' || this.formats.ClientCredentials === 'paseto') && !runtimeSupport.EdDSA) { - throw new TypeError('paseto structured tokens can only be enabled on Node.js >= 12.0.0 runtime'); - } - } - fixResponseTypes() { const types = new Set(); diff --git a/lib/helpers/defaults.js b/lib/helpers/defaults.js index fb6aa5c5b..f51558182 100644 --- a/lib/helpers/defaults.js +++ b/lib/helpers/defaults.js @@ -6,7 +6,6 @@ const os = require('os'); const MemoryAdapter = require('../adapters/memory_adapter'); const { DEV_KEYSTORE } = require('../consts'); -const runtimeSupport = require('./runtime_support'); const base64url = require('./base64url'); const attention = require('./attention'); const nanoid = require('./nanoid'); @@ -810,7 +809,6 @@ function getDefaults() { * description: Enables specific features from the Mutual TLS specification. The three main * features have their own specific setting in this feature's configuration object and * you must provide functions for resolving some of the functions which are deployment-specific. - * Note: **This feature is only supported in node runtime >= 12.0.0** * */ mTLS: { @@ -2302,7 +2300,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2322,7 +2320,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2342,7 +2340,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2363,7 +2361,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2384,7 +2382,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2405,7 +2403,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2426,7 +2424,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2446,7 +2444,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2462,7 +2460,7 @@ function getDefaults() { * example: Supported values list * ```js * [ - * // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + * // asymmetric RSAES based * 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', * // asymmetric ECDH-ES based * 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -2488,7 +2486,7 @@ function getDefaults() { * example: Supported values list * ```js * [ - * // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + * // asymmetric RSAES based * 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', * // asymmetric ECDH-ES based * 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -2513,7 +2511,7 @@ function getDefaults() { * example: Supported values list * ```js * [ - * // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + * // asymmetric RSAES based * 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', * // asymmetric ECDH-ES based * 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -2539,7 +2537,7 @@ function getDefaults() { * example: Supported values list * ```js * [ - * // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + * // asymmetric RSAES based * 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', * // asymmetric ECDH-ES based * 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -2565,7 +2563,7 @@ function getDefaults() { * example: Supported values list * ```js * [ - * // asymmetric RSAES based (note: RSA-OAEP-* is only supported in node runtime >= 12.9.0) + * // asymmetric RSAES based * 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', * // asymmetric ECDH-ES based * 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', @@ -2674,7 +2672,7 @@ function getDefaults() { * 'RS256', 'RS384', 'RS512', * 'PS256', 'PS384', 'PS512', * 'ES256', 'ES256K', 'ES384', 'ES512', - * 'EdDSA', // (note: EdDSA is only supported in node runtime >= 12.0.0) + * 'EdDSA', * ] * ``` */ @@ -2688,15 +2686,6 @@ function getDefaults() { logoutSource: undefined, }; - if (!runtimeSupport.EdDSA) { - Object.values(defaults.whitelistedJWA).forEach((algs) => { - const index = algs.indexOf('EdDSA'); - if (index !== -1) { - algs.splice(index, 1); - } - }); - } - /* * introspectionEndpointAuthMethods * diff --git a/lib/helpers/formatters.js b/lib/helpers/formatters.js index 4b3bf8f45..29ab4b73b 100644 --- a/lib/helpers/formatters.js +++ b/lib/helpers/formatters.js @@ -1,31 +1,15 @@ -let formatter; -if (Intl && Intl.ListFormat) { - const conjunction = new Intl.ListFormat('en', { type: 'conjunction' }); - const disjunction = new Intl.ListFormat('en', { type: 'disjunction' }); - formatter = { - format(iterable, { type }) { - if (type === 'conjunction') { - return conjunction.format(iterable); - } +const conjunction = new Intl.ListFormat('en', { type: 'conjunction' }); +const disjunction = new Intl.ListFormat('en', { type: 'disjunction' }); - return disjunction.format(iterable); - }, - }; -} else { - formatter = { - format(iterable, { type }) { - const last = iterable.pop(); - switch (iterable.length) { // length after pop; - case 0: - return last || ''; - case 1: - return `${iterable[0]} ${type === 'conjunction' ? 'and' : 'or'} ${last}`; - default: - return `${iterable.join(', ')}, ${type === 'conjunction' ? 'and' : 'or'} ${last}`; - } - }, - }; -} +const formatter = { + format(iterable, { type }) { + if (type === 'conjunction') { + return conjunction.format(iterable); + } + + return disjunction.format(iterable); + }, +}; module.exports = { formatList(list, { type = 'conjunction' } = {}) { diff --git a/lib/helpers/initialize_keystore.js b/lib/helpers/initialize_keystore.js index feb8f1efd..ccf4362f9 100644 --- a/lib/helpers/initialize_keystore.js +++ b/lib/helpers/initialize_keystore.js @@ -5,7 +5,6 @@ const hash = require('object-hash'); const { DEV_KEYSTORE } = require('../consts'); -const runtimeSupport = require('./runtime_support'); const attention = require('./attention'); const instance = require('./weak_cache'); @@ -62,10 +61,6 @@ provide your own in configuration "jwks" property'); throw new Error('keystore must be a JSON Web Key Set formatted object'); } - if (!runtimeSupport.shake256 && keystore.get({ kty: 'OKP', crv: 'Ed448' })) { - throw new Error('Ed448 keys are only fully supported to sign ID Tokens with in node runtime >= 12.8.0'); - } - instance(this).keystore = keystore; let warned; for (const key of keystore) { // eslint-disable-line no-restricted-syntax diff --git a/lib/helpers/request_uri_cache.js b/lib/helpers/request_uri_cache.js index b0156dd2c..996d00014 100644 --- a/lib/helpers/request_uri_cache.js +++ b/lib/helpers/request_uri_cache.js @@ -2,18 +2,17 @@ const crypto = require('crypto'); const { strict: assert } = require('assert'); const { STATUS_CODES } = require('http'); -const LRU = require('lru-cache'); +const Cache = require('lru-cache'); const epochTime = require('./epoch_time'); const request = require('./request'); class RequestUriCache { constructor(provider) { - this.cache = new LRU(100); + this.cache = new Cache(100); this.provider = provider; } - // TODO: in v7.x remove this async resolveUrn(requestUri) { // eslint-disable-line throw new Error('resolving request_uri by URN is not implemented'); } diff --git a/lib/helpers/runtime_support.js b/lib/helpers/runtime_support.js deleted file mode 100644 index fd76879f9..000000000 --- a/lib/helpers/runtime_support.js +++ /dev/null @@ -1,12 +0,0 @@ -const crypto = require('crypto'); - -const [major, minor] = process.version.substr(1).split('.').map((x) => parseInt(x, 10)); -const xofOutputLength = major > 12 || (major === 12 && minor >= 8); -const shake256 = xofOutputLength && crypto.getHashes().includes('shake256'); - -module.exports = { - oaepHash: major > 12 || (major === 12 && minor >= 9), - EdDSA: major >= 12, - KeyObject: typeof crypto.KeyObject !== 'undefined', - shake256, -}; diff --git a/lib/models/client.js b/lib/models/client.js index 5dea0040a..aa6475bc9 100644 --- a/lib/models/client.js +++ b/lib/models/client.js @@ -5,14 +5,13 @@ const { strict: assert } = require('assert'); const hash = require('object-hash'); const jose = require('jose'); -const LRU = require('lru-cache'); +const Cache = require('lru-cache'); const pick = require('../helpers/_/pick'); const snakeCase = require('../helpers/_/snake_case'); const mapKeys = require('../helpers/_/map_keys'); const camelCase = require('../helpers/_/camel_case'); const isPlainObject = require('../helpers/_/is_plain_object'); -const runtimeSupport = require('../helpers/runtime_support'); const base64url = require('../helpers/base64url'); const request = require('../helpers/request'); const nanoid = require('../helpers/nanoid'); @@ -25,16 +24,11 @@ const sectorIdentifier = require('../helpers/sector_identifier'); const { LOOPBACKS } = require('../consts/client_attributes'); // intentionally ignore x5t and x5t#S256 so that they are left to be calculated by the jose library -const KEY_ATTRIBUTES = ['crv', 'e', 'kid', 'kty', 'n', 'use', 'key_ops', 'x', 'y']; -const KEY_TYPES = new Set(['RSA', 'EC', 'oct']); +const KEY_ATTRIBUTES = ['crv', 'e', 'kid', 'kty', 'n', 'use', 'key_ops', 'x', 'y', 'x5c']; +const KEY_TYPES = new Set(['RSA', 'EC', 'OKP', 'oct']); const EC_CURVES = new Set(['P-256', 'secp256k1', 'P-384', 'P-521']); const OKP_SUBTYPES = new Set(['Ed25519', 'Ed448', 'X25519', 'X448']); -if (runtimeSupport.KeyObject) { - KEY_TYPES.add('OKP'); - KEY_ATTRIBUTES.push('x5c'); -} - const fingerprint = (properties) => hash(properties, { algorithm: 'sha256', ignoreUnknown: true, @@ -225,7 +219,7 @@ function deriveKey(secret, length) { module.exports = function getClient(provider) { const staticCache = new Map(); - const dynamicCache = new LRU(100); + const dynamicCache = new Cache(100); const Schema = getSchema(provider); const { IdToken } = provider; let adapter; diff --git a/lib/models/formats/paseto.js b/lib/models/formats/paseto.js index d5512cabd..56ac0ebb3 100644 --- a/lib/models/formats/paseto.js +++ b/lib/models/formats/paseto.js @@ -1,7 +1,6 @@ const { strict: assert } = require('assert'); const { createPrivateKey } = require('crypto'); -const runtimeSupport = require('../../helpers/runtime_support'); const paseto = require('../../helpers/paseto'); const instance = require('../../helpers/weak_cache'); const nanoid = require('../../helpers/nanoid'); @@ -28,9 +27,6 @@ module.exports = (provider, { opaque }) => { return nanoid(); }, async getValueAndPayload() { - if (!runtimeSupport.EdDSA) { - throw new Error('paseto structured tokens can only be enabled on Node.js >= 12.0.0 runtime'); - } const [, payload] = await opaque.getValueAndPayload.call(this); const { jti, iat, exp, scope, clientId, 'x5t#S256': x5t, jkt, extra, diff --git a/lib/models/id_token.js b/lib/models/id_token.js index e12415430..d31d85a3c 100644 --- a/lib/models/id_token.js +++ b/lib/models/id_token.js @@ -61,7 +61,6 @@ module.exports = function getIdToken(provider) { return merge({}, await mask.result(), this.extra); } - // TODO: in v7.x remove the use default async issue({ use = 'idtoken', expiresAt = null } = {}) { const { client } = this; const expiresIn = expiresAt ? expiresAt - epochTime() : undefined; diff --git a/package.json b/package.json index f1bbf40c6..d6eb83ea7 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "timekeeper": "^2.2.0" }, "engines": { - "node": "^10.13.0 || >=12.0.0" + "node": "^12.19.0 || ^14.15.0" }, "nyc": { "cache": false, diff --git a/test/certificate_bound_access_tokens/certificate_bound_access_tokens.test.js b/test/certificate_bound_access_tokens/certificate_bound_access_tokens.test.js index d585a0926..37fc1036f 100644 --- a/test/certificate_bound_access_tokens/certificate_bound_access_tokens.test.js +++ b/test/certificate_bound_access_tokens/certificate_bound_access_tokens.test.js @@ -4,14 +4,12 @@ const url = require('url'); const sinon = require('sinon'); const { expect } = require('chai'); -const runtimeSupport = require('../../lib/helpers/runtime_support'); const bootstrap = require('../test_helper'); const crt = readFileSync('./test/jwks/client.crt').toString(); const expectedS256 = 'eXvgMeO-8uLw0FGYkJefOXSFHOnbbcfv95rIYCPsbpo'; describe('features.mTLS.certificateBoundAccessTokens', () => { - if (!runtimeSupport.KeyObject) return; before(bootstrap(__dirname)); describe('discovery', () => { diff --git a/test/ci.js b/test/ci.js index 2de3709a6..a128261c6 100644 --- a/test/ci.js +++ b/test/ci.js @@ -1,8 +1,6 @@ /* eslint-disable no-restricted-syntax, max-len, no-await-in-loop, no-plusplus */ const { spawn } = require('child_process'); -const runtimeSupport = require('../lib/helpers/runtime_support'); - let first = true; function pass({ format, mountTo, mountVia } = {}) { @@ -50,7 +48,7 @@ function report() { } (async () => { - const formats = ['opaque', 'jwt', 'jwt-ietf', runtimeSupport.EdDSA ? 'paseto' : '', 'dynamic'].filter(Boolean); + const formats = ['opaque', 'jwt', 'jwt-ietf', 'paseto', 'dynamic']; for (const format of formats) { await pass({ format }); diff --git a/test/client_auth/client_auth.config.js b/test/client_auth/client_auth.config.js index 63d2b8956..d8653f0e4 100644 --- a/test/client_auth/client_auth.config.js +++ b/test/client_auth/client_auth.config.js @@ -6,7 +6,6 @@ const { e, n, kid, kty, use, } = require('../client.sig.key'); const mtlsKeys = require('../jwks/jwks.json'); -const runtimeSupport = require('../../lib/helpers/runtime_support'); const clientKey = { e, n, kid, kty, use, @@ -20,15 +19,13 @@ config.tokenEndpointAuthMethods = [ 'client_secret_post', 'private_key_jwt', 'client_secret_jwt', - ...(runtimeSupport.KeyObject ? [ - 'tls_client_auth', - 'self_signed_tls_client_auth', - ] : []), + 'tls_client_auth', + 'self_signed_tls_client_auth', ]; merge(config.features, { introspection: { enabled: true }, mTLS: { - enabled: runtimeSupport.KeyObject, + enabled: true, selfSignedTlsClientAuth: true, tlsClientAuth: true, getCertificate(ctx) { diff --git a/test/client_auth/client_auth.test.js b/test/client_auth/client_auth.test.js index 00b7af1f7..bb3a03ccd 100644 --- a/test/client_auth/client_auth.test.js +++ b/test/client_auth/client_auth.test.js @@ -7,7 +7,6 @@ const sinon = require('sinon'); const { expect } = require('chai'); const cloneDeep = require('lodash/cloneDeep'); -const runtimeSupport = require('../../lib/helpers/runtime_support'); const nanoid = require('../../lib/helpers/nanoid'); const { Provider } = require('../../lib'); const bootstrap = require('../test_helper'); @@ -101,8 +100,8 @@ describe('client authentication options', () => { 'ES256K', 'ES384', 'ES512', - runtimeSupport.EdDSA ? 'EdDSA' : false, - ].filter(Boolean); + 'EdDSA', + ]; expect(i(provider).configuration('tokenEndpointAuthSigningAlgValues')).to.eql(algs); expect(i(provider).configuration('introspectionEndpointAuthSigningAlgValues')).to.eql(algs); @@ -135,8 +134,8 @@ describe('client authentication options', () => { 'ES256K', 'ES384', 'ES512', - runtimeSupport.EdDSA ? 'EdDSA' : false, - ].filter(Boolean); + 'EdDSA', + ]; expect(i(provider).configuration('tokenEndpointAuthSigningAlgValues')).to.eql(algs); expect(i(provider).configuration('introspectionEndpointAuthSigningAlgValues')).to.eql(algs); @@ -1138,116 +1137,114 @@ describe('client authentication options', () => { }); }); - if (runtimeSupport.KeyObject) { - describe('tls_client_auth auth', () => { - it('accepts the auth', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) - .set('x-ssl-client-verify', 'SUCCESS') - .set('x-ssl-client-san-dns', 'rp.example.com') - .send({ - client_id: 'client-pki-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthSucceeded); - }); + describe('tls_client_auth auth', () => { + it('accepts the auth', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) + .set('x-ssl-client-verify', 'SUCCESS') + .set('x-ssl-client-san-dns', 'rp.example.com') + .send({ + client_id: 'client-pki-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthSucceeded); + }); - it('fails the auth when getCertificate() does not return a cert', function () { - return this.agent.post(route) - .send({ - client_id: 'client-pki-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthRejected); - }); + it('fails the auth when getCertificate() does not return a cert', function () { + return this.agent.post(route) + .send({ + client_id: 'client-pki-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthRejected); + }); - it('fails the auth when certificateAuthorized() fails', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) - .set('x-ssl-client-verify', 'FAILED: self signed certificate') - .set('x-ssl-client-san-dns', 'rp.example.com') - .send({ - client_id: 'client-pki-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthRejected); - }); + it('fails the auth when certificateAuthorized() fails', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) + .set('x-ssl-client-verify', 'FAILED: self signed certificate') + .set('x-ssl-client-san-dns', 'rp.example.com') + .send({ + client_id: 'client-pki-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthRejected); + }); - it('fails the auth when certificateSubjectMatches() return false', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) - .set('x-ssl-client-verify', 'SUCCESS') - .set('x-ssl-client-san-dns', 'foobarbaz') - .send({ - client_id: 'client-pki-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthRejected); - }); + it('fails the auth when certificateSubjectMatches() return false', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) + .set('x-ssl-client-verify', 'SUCCESS') + .set('x-ssl-client-san-dns', 'foobarbaz') + .send({ + client_id: 'client-pki-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthRejected); }); + }); - describe('self_signed_tls_client_auth auth', () => { - it('accepts the auth [1/2]', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) - .send({ - client_id: 'client-self-signed-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthSucceeded); - }); + describe('self_signed_tls_client_auth auth', () => { + it('accepts the auth [1/2]', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) + .send({ + client_id: 'client-self-signed-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthSucceeded); + }); - it('accepts the auth [2/2]', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', eccrt.replace(RegExp('\\r?\\n', 'g'), '')) - .send({ - client_id: 'client-self-signed-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthSucceeded); - }); + it('accepts the auth [2/2]', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', eccrt.replace(RegExp('\\r?\\n', 'g'), '')) + .send({ + client_id: 'client-self-signed-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthSucceeded); + }); - it('fails the auth when x-ssl-client-cert is not passed by the proxy', function () { - return this.agent.post(route) - .send({ - client_id: 'client-self-signed-mtls', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthRejected); - }); + it('fails the auth when x-ssl-client-cert is not passed by the proxy', function () { + return this.agent.post(route) + .send({ + client_id: 'client-self-signed-mtls', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthRejected); + }); - it('fails the auth when x-ssl-client-cert does not match the registered ones', function () { - return this.agent.post(route) - .set('x-ssl-client-cert', eccrt.replace(RegExp('\\r?\\n', 'g'), '')) - .send({ - client_id: 'client-self-signed-mtls-rsa', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthRejected); - }); + it('fails the auth when x-ssl-client-cert does not match the registered ones', function () { + return this.agent.post(route) + .set('x-ssl-client-cert', eccrt.replace(RegExp('\\r?\\n', 'g'), '')) + .send({ + client_id: 'client-self-signed-mtls-rsa', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthRejected); + }); - it('handles rotation of stale jwks', function () { - nock('https://client.example.com/') - .get('/jwks') - .reply(200, JSON.stringify(mtlsKeys)); + it('handles rotation of stale jwks', function () { + nock('https://client.example.com/') + .get('/jwks') + .reply(200, JSON.stringify(mtlsKeys)); - return this.agent.post(route) - .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) - .send({ - client_id: 'client-self-signed-mtls-jwks_uri', - grant_type: 'implicit', - }) - .type('form') - .expect(tokenAuthSucceeded); - }); + return this.agent.post(route) + .set('x-ssl-client-cert', rsacrt.replace(RegExp('\\r?\\n', 'g'), '')) + .send({ + client_id: 'client-self-signed-mtls-jwks_uri', + grant_type: 'implicit', + }) + .type('form') + .expect(tokenAuthSucceeded); }); - } + }); }); diff --git a/test/configuration/client_keystore.test.js b/test/configuration/client_keystore.test.js index dcda1b76a..b2fec2f38 100644 --- a/test/configuration/client_keystore.test.js +++ b/test/configuration/client_keystore.test.js @@ -7,7 +7,6 @@ const sinon = require('sinon').createSandbox(); const { expect } = require('chai'); const cloneDeep = require('lodash/cloneDeep'); -const runtimeSupport = require('../../lib/helpers/runtime_support'); const mtlsKeys = require('../jwks/jwks.json'); const JWT = require('../../lib/helpers/jwt'); const epochTime = require('../../lib/helpers/epoch_time'); @@ -143,20 +142,18 @@ describe('client keystore refresh', () => { }); }); - if (runtimeSupport.KeyObject) { - it('does x5c validation', async function () { - setResponse(invalidx5c); + it('does x5c validation', async function () { + setResponse(invalidx5c); - const client = await this.provider.Client.find('client'); - sinon.stub(client.keystore, 'fresh').returns(false); - await client.keystore.refresh().then(fail, (err) => { - expect(err).to.be.an('error'); - expect(err.message).to.equal('invalid_client_metadata'); - expect(err.error_description).to.match(/jwks_uri could not be refreshed/); - expect(err.error_description).to.match(/must be an array of one or more PKIX certificates when provided/); - }); + const client = await this.provider.Client.find('client'); + sinon.stub(client.keystore, 'fresh').returns(false); + await client.keystore.refresh().then(fail, (err) => { + expect(err).to.be.an('error'); + expect(err.message).to.equal('invalid_client_metadata'); + expect(err.error_description).to.match(/jwks_uri could not be refreshed/); + expect(err.error_description).to.match(/must be an array of one or more PKIX certificates when provided/); }); - } + }); describe('caching', () => { it('uses expires caching header to determine stale states', async function () { diff --git a/test/configuration/client_metadata.test.js b/test/configuration/client_metadata.test.js index d4d363e7d..4a83ab090 100644 --- a/test/configuration/client_metadata.test.js +++ b/test/configuration/client_metadata.test.js @@ -7,7 +7,6 @@ const omit = require('lodash/omit'); const pull = require('lodash/pull'); const cloneDeep = require('lodash/cloneDeep'); -const runtimeSupport = require('../../lib/helpers/runtime_support'); const { Provider } = require('../../lib'); const { whitelistedJWA } = require('../default.config'); const mtlsKeys = require('../jwks/jwks.json'); @@ -372,8 +371,8 @@ describe('Client metadata validation', () => { mustBeString(this.title); [ 'none', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', - 'PS256', 'PS384', 'PS512', 'ES256', 'ES384', 'ES512', runtimeSupport.EdDSA ? 'EdDSA' : false, - ].filter(Boolean).forEach((alg) => { + 'PS256', 'PS384', 'PS512', 'ES256', 'ES384', 'ES512', 'EdDSA', + ].forEach((alg) => { allows(this.title, alg, { jwks: { keys: [sigKey] } }); }); rejects(this.title, 'not-an-alg'); @@ -496,11 +495,11 @@ describe('Client metadata validation', () => { 'client_secret_post', 'private_key_jwt', 'client_secret_jwt', - runtimeSupport.KeyObject ? 'tls_client_auth' : false, - ].filter(Boolean), + 'tls_client_auth', + ], features: { mTLS: { - enabled: runtimeSupport.KeyObject, selfSignedTlsClientAuth: true, tlsClientAuth: true, + enabled: true, selfSignedTlsClientAuth: true, tlsClientAuth: true, }, }, }; @@ -527,28 +526,26 @@ describe('Client metadata validation', () => { }, configuration); break; case 'tls_client_auth': - if (runtimeSupport.KeyObject) { - allows(this.title, value, { - tls_client_auth_subject_dn: 'foo', - }, configuration); - allows(this.title, value, { - tls_client_auth_san_dns: 'foo', - }, configuration); - allows(this.title, value, { - tls_client_auth_san_uri: 'foo', - }, configuration); - allows(this.title, value, { - tls_client_auth_san_ip: 'foo', - }, configuration); - allows(this.title, value, { - tls_client_auth_san_email: 'foo', - }, configuration); - rejects(this.title, value, 'tls_client_auth requires one of the certificate subject value parameters', undefined, configuration); - rejects(this.title, value, 'only one tls_client_auth certificate subject value must be provided', { - tls_client_auth_san_ip: 'foo', - tls_client_auth_san_email: 'foo', - }, configuration); - } + allows(this.title, value, { + tls_client_auth_subject_dn: 'foo', + }, configuration); + allows(this.title, value, { + tls_client_auth_san_dns: 'foo', + }, configuration); + allows(this.title, value, { + tls_client_auth_san_uri: 'foo', + }, configuration); + allows(this.title, value, { + tls_client_auth_san_ip: 'foo', + }, configuration); + allows(this.title, value, { + tls_client_auth_san_email: 'foo', + }, configuration); + rejects(this.title, value, 'tls_client_auth requires one of the certificate subject value parameters', undefined, configuration); + rejects(this.title, value, 'only one tls_client_auth certificate subject value must be provided', { + tls_client_auth_san_ip: 'foo', + tls_client_auth_san_email: 'foo', + }, configuration); break; default: { allows(this.title, value, undefined, configuration); @@ -646,10 +643,10 @@ describe('Client metadata validation', () => { })); allows(this.title, 'dir', undefined, configuration); [ - 'RSA-OAEP', ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', + 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'A128KW', 'A192KW', 'A256KW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', - ].filter(Boolean).forEach((value) => { + ].forEach((value) => { allows(this.title, value, { jwks: { keys: [sigKey] }, }, configuration); @@ -698,10 +695,10 @@ describe('Client metadata validation', () => { })); allows(this.title, 'dir', undefined, configuration); [ - 'RSA-OAEP', ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', + 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'A128KW', 'A192KW', 'A256KW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', - ].filter(Boolean).forEach((value) => { + ].forEach((value) => { allows(this.title, value, { jwks: { keys: [sigKey] }, }, configuration); @@ -751,10 +748,10 @@ describe('Client metadata validation', () => { })); allows(this.title, 'dir', undefined, configuration); [ - 'RSA-OAEP', ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', + 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'A128KW', 'A192KW', 'A256KW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', - ].filter(Boolean).forEach((value) => { + ].forEach((value) => { allows(this.title, value, { jwks: { keys: [sigKey] }, }, configuration); @@ -804,10 +801,10 @@ describe('Client metadata validation', () => { })); allows(this.title, 'dir', undefined, configuration); [ - 'RSA-OAEP', ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', + 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'A128KW', 'A192KW', 'A256KW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', - ].filter(Boolean).forEach((value) => { + ].forEach((value) => { allows(this.title, value, { jwks: { keys: [sigKey] }, }, configuration); @@ -863,10 +860,10 @@ describe('Client metadata validation', () => { })); allows(this.title, 'dir', undefined, configuration); [ - 'RSA-OAEP', ...(runtimeSupport.oaepHash ? ['RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512'] : []), 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', + 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', 'RSA-OAEP-512', 'RSA1_5', 'ECDH-ES', 'ECDH-ES+A128KW', 'ECDH-ES+A192KW', 'ECDH-ES+A256KW', 'A128GCMKW', 'A192GCMKW', 'A256GCMKW', 'A128KW', 'A192KW', 'A256KW', 'PBES2-HS256+A128KW', 'PBES2-HS384+A192KW', 'PBES2-HS512+A256KW', - ].filter(Boolean).forEach((value) => { + ].forEach((value) => { allows(this.title, value, undefined, configuration); }); rejects(this.title, 'not-an-alg', undefined, undefined, configuration); @@ -956,19 +953,17 @@ describe('Client metadata validation', () => { request_object_signing_alg: 'ES384', }); - if (runtimeSupport.KeyObject) { - const invalidx5c = cloneDeep(mtlsKeys); - invalidx5c.keys[0].x5c = true; - rejects(this.title, invalidx5c, 'jwks keys member index 0 is not a valid EC JWK (`x5c` must be an array of one or more PKIX certificates when provided)'); + const invalidx5c = cloneDeep(mtlsKeys); + invalidx5c.keys[0].x5c = true; + rejects(this.title, invalidx5c, 'jwks keys member index 0 is not a valid EC JWK (`x5c` must be an array of one or more PKIX certificates when provided)'); - const emptyx5c = cloneDeep(mtlsKeys); - emptyx5c.keys[0].x5c = []; - rejects(this.title, emptyx5c, 'jwks keys member index 0 is not a valid EC JWK (`x5c` must be an array of one or more PKIX certificates when provided)'); + const emptyx5c = cloneDeep(mtlsKeys); + emptyx5c.keys[0].x5c = []; + rejects(this.title, emptyx5c, 'jwks keys member index 0 is not a valid EC JWK (`x5c` must be an array of one or more PKIX certificates when provided)'); - const invalidCert = cloneDeep(mtlsKeys); - invalidCert.keys[0].x5c = ['foobar']; - rejects(this.title, invalidCert, 'jwks keys member index 0 is not a valid EC JWK (`x5c` member at index 0 is not a valid base64-encoded DER PKIX certificate)'); - } + const invalidCert = cloneDeep(mtlsKeys); + invalidCert.keys[0].x5c = ['foobar']; + rejects(this.title, invalidCert, 'jwks keys member index 0 is not a valid EC JWK (`x5c` member at index 0 is not a valid base64-encoded DER PKIX certificate)'); [ 'id_token_encrypted_response_alg', @@ -989,21 +984,19 @@ describe('Client metadata validation', () => { }); }); - if (runtimeSupport.KeyObject) { - context('features.mTLS.certificateBoundAccessTokens', () => { - context('tls_client_certificate_bound_access_tokens', function () { - const configuration = { - features: { - mTLS: { enabled: true, certificateBoundAccessTokens: true }, - }, - }; + context('features.mTLS.certificateBoundAccessTokens', () => { + context('tls_client_certificate_bound_access_tokens', function () { + const configuration = { + features: { + mTLS: { enabled: true, certificateBoundAccessTokens: true }, + }, + }; - defaultsTo(this.title, false, undefined, configuration); - defaultsTo(this.title, undefined); - mustBeBoolean(this.title, undefined, configuration); - }); + defaultsTo(this.title, false, undefined, configuration); + defaultsTo(this.title, undefined); + mustBeBoolean(this.title, undefined, configuration); }); - } + }); context('features.backchannelLogout', () => { const configuration = { @@ -1056,87 +1049,85 @@ describe('Client metadata validation', () => { tokenEndpointAuthMethods: ['tls_client_auth', 'client_secret_basic'], }; - if (runtimeSupport.KeyObject) { - context('tls_client_auth_subject_dn', function () { - mustBeString(this.title, undefined, undefined, configuration); - allows(this.title, 'foo', { - token_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - revocation_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - introspection_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', undefined, configuration, (client) => { - expect(client.metadata()[this.title]).to.eql(undefined); - }); + context('tls_client_auth_subject_dn', function () { + mustBeString(this.title, undefined, undefined, configuration); + allows(this.title, 'foo', { + token_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + revocation_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + introspection_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', undefined, configuration, (client) => { + expect(client.metadata()[this.title]).to.eql(undefined); }); + }); - context('tls_client_auth_san_dns', function () { - mustBeString(this.title, undefined, undefined, configuration); - allows(this.title, 'foo', { - token_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - revocation_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - introspection_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', undefined, configuration, (client) => { - expect(client.metadata()[this.title]).to.eql(undefined); - }); + context('tls_client_auth_san_dns', function () { + mustBeString(this.title, undefined, undefined, configuration); + allows(this.title, 'foo', { + token_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + revocation_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + introspection_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', undefined, configuration, (client) => { + expect(client.metadata()[this.title]).to.eql(undefined); }); + }); - context('tls_client_auth_san_uri', function () { - mustBeString(this.title, undefined, undefined, configuration); - allows(this.title, 'foo', { - token_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - revocation_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - introspection_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', undefined, configuration, (client) => { - expect(client.metadata()[this.title]).to.eql(undefined); - }); + context('tls_client_auth_san_uri', function () { + mustBeString(this.title, undefined, undefined, configuration); + allows(this.title, 'foo', { + token_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + revocation_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + introspection_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', undefined, configuration, (client) => { + expect(client.metadata()[this.title]).to.eql(undefined); }); + }); - context('tls_client_auth_san_ip', function () { - mustBeString(this.title, undefined, undefined, configuration); - allows(this.title, 'foo', { - token_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - revocation_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - introspection_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', undefined, configuration, (client) => { - expect(client.metadata()[this.title]).to.eql(undefined); - }); + context('tls_client_auth_san_ip', function () { + mustBeString(this.title, undefined, undefined, configuration); + allows(this.title, 'foo', { + token_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + revocation_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + introspection_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', undefined, configuration, (client) => { + expect(client.metadata()[this.title]).to.eql(undefined); }); + }); - context('tls_client_auth_san_email', function () { - mustBeString(this.title, undefined, undefined, configuration); - allows(this.title, 'foo', { - token_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - revocation_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', { - introspection_endpoint_auth_method: 'tls_client_auth', - }, configuration); - allows(this.title, 'foo', undefined, configuration, (client) => { - expect(client.metadata()[this.title]).to.eql(undefined); - }); + context('tls_client_auth_san_email', function () { + mustBeString(this.title, undefined, undefined, configuration); + allows(this.title, 'foo', { + token_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + revocation_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', { + introspection_endpoint_auth_method: 'tls_client_auth', + }, configuration); + allows(this.title, 'foo', undefined, configuration, (client) => { + expect(client.metadata()[this.title]).to.eql(undefined); }); - } + }); } context('jwks_uri', function () { diff --git a/test/configuration/keystore_configuration.test.js b/test/configuration/keystore_configuration.test.js index 736e70345..0fc47cc66 100644 --- a/test/configuration/keystore_configuration.test.js +++ b/test/configuration/keystore_configuration.test.js @@ -5,7 +5,6 @@ const sinon = require('sinon').createSandbox(); const { expect } = require('chai'); const { Provider } = require('../../lib'); -const { EdDSA, shake256 } = require('../../lib/helpers/runtime_support'); describe('configuration.jwks', () => { beforeEach(function () { @@ -78,22 +77,4 @@ describe('configuration.jwks', () => { new Provider('http://localhost', { jwks: ks.toJWKS(true) }); }).not.to.throw(); }); - - if (EdDSA) { - it('only enables Ed448 in Node.js >= 12.8.0', async () => { - const ks = new jose.JWKS.KeyStore(); - ks.add(global.keystore.get({ alg: 'RS256' })); - await ks.generate('OKP', 'Ed448'); - - if (shake256) { - expect(() => { - new Provider('http://localhost', { jwks: ks.toJWKS(true) }); - }).not.to.throw(); - } else { - expect(() => { - new Provider('http://localhost', { jwks: ks.toJWKS(true) }); - }).to.throw('Ed448 keys are only fully supported to sign ID Tokens with in node runtime >= 12.8.0'); - } - }); - } }); diff --git a/test/formats/noed25519.config.js b/test/formats/noed25519.config.js deleted file mode 100644 index 8360bec08..000000000 --- a/test/formats/noed25519.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const cloneDeep = require('lodash/cloneDeep'); - -const config = cloneDeep(require('../default.config')); - -config.jwks = undefined; - -module.exports = { - config, -}; diff --git a/test/node10/node10.test.js b/test/node10/node10.test.js deleted file mode 100644 index a9b41ff75..000000000 --- a/test/node10/node10.test.js +++ /dev/null @@ -1,42 +0,0 @@ -const { expect } = require('chai'); - -const { Provider } = require('../../lib'); - -const [major] = process.version.substr(1).split('.').map((x) => parseInt(x, 10)); - -if (major === 10) { - describe('node 10 unsupported features', () => { - it('mtls can not be enabled', () => { - expect(() => { - new Provider('http://localhost', { // eslint-disable-line no-new - features: { - mTLS: { enabled: true }, - }, - }); - }).to.throw('mTLS can only be enabled on Node.js >= 12.0.0 runtime'); - }); - - it('paseto can not be enabled', () => { - expect(() => { - new Provider('http://localhost', { // eslint-disable-line no-new - formats: { AccessToken: 'paseto' }, - }); - }).to.throw('paseto structured tokens can only be enabled on Node.js >= 12.0.0 runtime'); - }); - - it('dynamic paseto results in an Error', async () => { - const provider = new Provider('http://localhost', { // eslint-disable-line no-new - formats: { AccessToken() { return 'paseto'; } }, - }); - - const accessToken = new provider.AccessToken(); - - try { - await accessToken.save(); - throw new Error('expexted save to throw'); - } catch (err) { - expect(err.message).to.eql('paseto structured tokens can only be enabled on Node.js >= 12.0.0 runtime'); - } - }); - }); -} diff --git a/test/run.js b/test/run.js index e7f84f9a3..29a46a65a 100644 --- a/test/run.js +++ b/test/run.js @@ -7,8 +7,6 @@ const lookupFiles = require('mocha/lib/cli/lookup-files'); const { all: clearRequireCache } = require('clear-module'); const sample = require('lodash/sample'); -const runtimeSupport = require('../lib/helpers/runtime_support'); - const FORMAT_REGEXP = /^--format=([\w-]+)$/; const formats = []; @@ -22,9 +20,7 @@ if (!formats.length) { formats.push('opaque'); formats.push('jwt'); formats.push('jwt-ietf'); - if (runtimeSupport.EdDSA) { - formats.push('paseto'); - } + formats.push('paseto'); formats.push('dynamic'); } const passed = []; @@ -47,7 +43,7 @@ async function singleRun() { await Promise.all([ global.keystore.generate('RSA', 2048), global.keystore.generate('EC', 'P-256'), - runtimeSupport.EdDSA ? global.keystore.generate('OKP', 'Ed25519') : undefined, + global.keystore.generate('OKP', 'Ed25519'), ]); global.TEST_CONFIGURATION_DEFAULTS = {}; if (this.format === 'jwt-ietf' || typeof this.format === 'function') { @@ -113,7 +109,7 @@ async function singleRun() { await singleRun.call({ format: 'paseto' }); } if (formats.includes('dynamic')) { - await singleRun.call({ format: () => sample(['opaque', 'jwt', 'jwt-ietf', runtimeSupport.EdDSA ? 'paseto' : undefined].filter(Boolean)) }); + await singleRun.call({ format: () => sample(['opaque', 'jwt', 'jwt-ietf', 'paseto']) }); } passed.forEach((pass) => console.log('\x1b[32m%s\x1b[0m', pass)); })() diff --git a/test/signatures/signatures.test.js b/test/signatures/signatures.test.js index 5ca99cf20..f0595cfe9 100644 --- a/test/signatures/signatures.test.js +++ b/test/signatures/signatures.test.js @@ -7,7 +7,6 @@ const nanoid = require('../../lib/helpers/nanoid'); const bootstrap = require('../test_helper'); const { decode } = require('../../lib/helpers/jwt'); const epochTime = require('../../lib/helpers/epoch_time'); -const { EdDSA, shake256 } = require('../../lib/helpers/runtime_support'); const { formats: { AccessToken: FORMAT } } = global.TEST_CONFIGURATION_DEFAULTS; @@ -26,51 +25,47 @@ describe('signatures', () => { this.client.idTokenSignedResponseAlg = 'RS256'; }); - if (EdDSA) { - it('responds with a access_token and code (half of sha512 Ed25519)', function () { - this.client.idTokenSignedResponseAlg = 'EdDSA'; - const auth = new this.AuthorizationRequest({ - response_type: 'code id_token token', - scope: 'openid', + it('responds with a access_token and code (half of sha512 Ed25519)', function () { + this.client.idTokenSignedResponseAlg = 'EdDSA'; + const auth = new this.AuthorizationRequest({ + response_type: 'code id_token token', + scope: 'openid', + }); + + return this.wrap({ auth, verb: 'get', route: '/auth' }) + .expect(302) + .expect(auth.validateFragment) + .expect(auth.validateClientLocation) + .expect((response) => { + const { query: { id_token } } = parseLocation(response.headers.location, true); + const { payload } = decode(id_token); + expect(payload).to.contain.keys('at_hash', 'c_hash'); + expect(payload.at_hash).to.have.lengthOf(43); }); + }); - return this.wrap({ auth, verb: 'get', route: '/auth' }) - .expect(302) - .expect(auth.validateFragment) - .expect(auth.validateClientLocation) - .expect((response) => { - const { query: { id_token } } = parseLocation(response.headers.location, true); - const { payload } = decode(id_token); - expect(payload).to.contain.keys('at_hash', 'c_hash'); - expect(payload.at_hash).to.have.lengthOf(43); - }); + it('responds with a access_token and code (half of shake256(m, 114) Ed448)', async function () { + this.client.idTokenSignedResponseAlg = 'EdDSA'; + const key = i(this.provider).keystore.get({ alg: 'EdDSA' }); + i(this.provider).keystore.remove(key); + await i(this.provider).keystore.generate('OKP', 'Ed448'); + await i(this.provider).keystore.generate('OKP', 'Ed25519'); + const auth = new this.AuthorizationRequest({ + response_type: 'code id_token token', + scope: 'openid', }); - if (shake256) { - it('responds with a access_token and code (half of shake256(m, 114) Ed448)', async function () { - this.client.idTokenSignedResponseAlg = 'EdDSA'; - const key = i(this.provider).keystore.get({ alg: 'EdDSA' }); - i(this.provider).keystore.remove(key); - await i(this.provider).keystore.generate('OKP', 'Ed448'); - await i(this.provider).keystore.generate('OKP', 'Ed25519'); - const auth = new this.AuthorizationRequest({ - response_type: 'code id_token token', - scope: 'openid', - }); - - return this.wrap({ auth, verb: 'get', route: '/auth' }) - .expect(302) - .expect(auth.validateFragment) - .expect(auth.validateClientLocation) - .expect((response) => { - const { query: { id_token } } = parseLocation(response.headers.location, true); - const { payload } = decode(id_token); - expect(payload).to.contain.keys('at_hash', 'c_hash'); - expect(payload.at_hash).to.have.lengthOf(76); - }); + return this.wrap({ auth, verb: 'get', route: '/auth' }) + .expect(302) + .expect(auth.validateFragment) + .expect(auth.validateClientLocation) + .expect((response) => { + const { query: { id_token } } = parseLocation(response.headers.location, true); + const { payload } = decode(id_token); + expect(payload).to.contain.keys('at_hash', 'c_hash'); + expect(payload.at_hash).to.have.lengthOf(76); }); - } - } + }); it('responds with a access_token and code (half of sha512)', function () { this.client.idTokenSignedResponseAlg = 'RS512'; diff --git a/test/trigger_conformance_build.js b/test/trigger_conformance_build.js index a983bf76c..750e84dbf 100644 --- a/test/trigger_conformance_build.js +++ b/test/trigger_conformance_build.js @@ -1,6 +1,6 @@ /* eslint-disable no-console, no-await-in-loop, no-loop-func */ -const got = require('got'); +const got = require('got'); // TODO: upgrade got const { TRAVIS_BUILD_NUMBER,