Comparing version 1.28.0 to 2.0.0
129
CHANGELOG.md
@@ -1,10 +0,71 @@ | ||
# Change Log | ||
# Changelog | ||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. | ||
# [1.28.0](https://github.com/panva/jose/compare/v1.27.3...v1.28.0) (2020-08-10) | ||
## [2.0.0](https://github.com/panva/jose/compare/v1.28.0...v2.0.0) (2020-09-08) | ||
### ⚠ BREAKING CHANGES | ||
* the `JWE.decrypt` option `algorithms` was removed and | ||
replaced with contentEncryptionAlgorithms (handles `enc` allowlist) and | ||
keyManagementAlgorithms (handles `alg` allowlist) | ||
* the `JWT.verify` profile option was removed, use e.g. | ||
`JWT.IdToken.verify` instead. | ||
* removed the `maxAuthAge` `JWT.verify` option, this | ||
option is now only present at the specific JWT profile APIs where the | ||
`auth_time` property applies. | ||
* removed the `nonce` `JWT.verify` option, this | ||
option is now only present at the specific JWT profile APIs where the | ||
`nonce` property applies. | ||
* the `acr`, `amr`, `nonce` and `azp` claim value types | ||
will only be checked when verifying a specific JWT profile using its | ||
dedicated API. | ||
* using the draft implementing APIs will emit a one-time | ||
warning per process using `process.emitWarning` | ||
* `JWT.sign` function options no longer accept a `nonce` | ||
property. To create a JWT with a `nonce` just pass the value to the | ||
payload. | ||
* due to added ESM module support Node.js version with | ||
ESM implementation bugs are no longer supported, this only affects early | ||
v13.x versions. The resulting Node.js semver range is | ||
`>=10.13.0 < 13 || >=13.7.0` | ||
* deprecated method `JWK.importKey` was removed | ||
* deprecated method `JWKS.KeyStore.fromJWKS` was removed | ||
* the use of unregistered curve name P-256K for secp256k1 | ||
was removed | ||
* jose.JWE.Encrypt constructor aad and unprotectedHeader | ||
arguments swapped places | ||
* jose.JWE.encrypt.flattened header (unprotectedHeader) | ||
and aad arguments swapped places | ||
* jose.JWE.encrypt.general header (unprotectedHeader) | ||
and aad arguments swapped places | ||
* JWS.verify returned payloads are now always buffers | ||
* JWS.verify options `encoding` and `parse` were removed | ||
### Features | ||
* added support for ESM (ECMAScript modules) ([1aa9035](https://github.com/panva/jose/commit/1aa9035552bbcb34b95e092d0f082cc6d94465ab)) | ||
* decrypt allowlists for both key management and content encryption ([30e5c46](https://github.com/panva/jose/commit/30e5c46ecf00a498e65a551ced88bc897531c2a4)) | ||
### Bug Fixes | ||
* **typescript:** allow Buffer when verifying detached signature ([cadbd04](https://github.com/panva/jose/commit/cadbd047ca953d6d8171439f2efd7bb98a5d8e73)) | ||
* **typescript:** properly type all decode/verify/decrypt fn options ([4c23bd6](https://github.com/panva/jose/commit/4c23bd65fe6fa634726a5eb73c6d590f7348a97e)) | ||
### Refactor | ||
* encrypt APIs unprotectedHeader and aad arguments swapped ([70bd4ae](https://github.com/panva/jose/commit/70bd4ae6b2e6ba94bbe0b3dc1a17b2990af3a18b)) | ||
* move JWT profile specifics outside of generic JWT ([fd69d7f](https://github.com/panva/jose/commit/fd69d7f5093d0b3a231d7d79aa3bca3a8a64464c)) | ||
* removed `nonce` option from `JWT.sign` ([c4267cc](https://github.com/panva/jose/commit/c4267cc655bc2721d846c98f8a40640d1a12e9ad)) | ||
* removed deprecated methods and utilities ([6c35c51](https://github.com/panva/jose/commit/6c35c519c9181f8246b36ad02572adb609d6de1d)) | ||
* removed payload parsing from JWS.verify ([ba5c897](https://github.com/panva/jose/commit/ba5c89791915a2a3cd56b3dab1f3328778152d33)) | ||
## [1.28.0](https://github.com/panva/jose/compare/v1.27.3...v1.28.0) (2020-08-10) | ||
### Features | ||
* support for validating issuer from a list of values ([#91](https://github.com/panva/jose/issues/91)) ([ce6836a](https://github.com/panva/jose/commit/ce6836af88c9e73c29560233f15ed1760c7dcc13)) | ||
@@ -42,3 +103,3 @@ | ||
# [1.27.0](https://github.com/panva/jose/compare/v1.26.1...v1.27.0) (2020-05-05) | ||
## [1.27.0](https://github.com/panva/jose/compare/v1.26.1...v1.27.0) (2020-05-05) | ||
@@ -62,3 +123,3 @@ | ||
# [1.26.0](https://github.com/panva/jose/compare/v1.25.2...v1.26.0) (2020-04-16) | ||
## [1.26.0](https://github.com/panva/jose/compare/v1.25.2...v1.26.0) (2020-04-16) | ||
@@ -96,3 +157,3 @@ | ||
# [1.25.0](https://github.com/panva/jose/compare/v1.24.1...v1.25.0) (2020-03-11) | ||
## [1.25.0](https://github.com/panva/jose/compare/v1.24.1...v1.25.0) (2020-03-11) | ||
@@ -115,3 +176,3 @@ | ||
# [1.24.0](https://github.com/panva/jose/compare/v1.23.0...v1.24.0) (2020-02-25) | ||
## [1.24.0](https://github.com/panva/jose/compare/v1.23.0...v1.24.0) (2020-02-25) | ||
@@ -125,3 +186,3 @@ | ||
# [1.23.0](https://github.com/panva/jose/compare/v1.22.2...v1.23.0) (2020-02-18) | ||
## [1.23.0](https://github.com/panva/jose/compare/v1.22.2...v1.23.0) (2020-02-18) | ||
@@ -159,3 +220,3 @@ | ||
# [1.22.0](https://github.com/panva/jose/compare/v1.21.1...v1.22.0) (2020-01-29) | ||
## [1.22.0](https://github.com/panva/jose/compare/v1.21.1...v1.22.0) (2020-01-29) | ||
@@ -183,3 +244,3 @@ | ||
# [1.21.0](https://github.com/panva/jose/compare/v1.20.0...v1.21.0) (2020-01-23) | ||
## [1.21.0](https://github.com/panva/jose/compare/v1.20.0...v1.21.0) (2020-01-23) | ||
@@ -198,3 +259,3 @@ | ||
# [1.20.0](https://github.com/panva/jose/compare/v1.19.0...v1.20.0) (2020-01-16) | ||
## [1.20.0](https://github.com/panva/jose/compare/v1.19.0...v1.20.0) (2020-01-16) | ||
@@ -208,3 +269,3 @@ | ||
# [1.19.0](https://github.com/panva/jose/compare/v1.18.2...v1.19.0) (2020-01-13) | ||
## [1.19.0](https://github.com/panva/jose/compare/v1.18.2...v1.19.0) (2020-01-13) | ||
@@ -238,3 +299,3 @@ | ||
# [1.18.0](https://github.com/panva/jose/compare/v1.17.2...v1.18.0) (2019-12-31) | ||
## [1.18.0](https://github.com/panva/jose/compare/v1.17.2...v1.18.0) (2019-12-31) | ||
@@ -266,3 +327,3 @@ | ||
# [1.17.0](https://github.com/panva/jose/compare/v1.16.2...v1.17.0) (2019-12-10) | ||
## [1.17.0](https://github.com/panva/jose/compare/v1.16.2...v1.17.0) (2019-12-10) | ||
@@ -294,3 +355,3 @@ | ||
# [1.16.0](https://github.com/panva/jose/compare/v1.15.1...v1.16.0) (2019-12-04) | ||
## [1.16.0](https://github.com/panva/jose/compare/v1.15.1...v1.16.0) (2019-12-04) | ||
@@ -313,3 +374,3 @@ | ||
# [1.15.0](https://github.com/panva/jose/compare/v1.14.0...v1.15.0) (2019-11-27) | ||
## [1.15.0](https://github.com/panva/jose/compare/v1.14.0...v1.15.0) (2019-11-27) | ||
@@ -328,3 +389,3 @@ | ||
# [1.14.0](https://github.com/panva/jose/compare/v1.13.0...v1.14.0) (2019-11-26) | ||
## [1.14.0](https://github.com/panva/jose/compare/v1.13.0...v1.14.0) (2019-11-26) | ||
@@ -338,3 +399,3 @@ | ||
# [1.13.0](https://github.com/panva/jose/compare/v1.12.1...v1.13.0) (2019-11-23) | ||
## [1.13.0](https://github.com/panva/jose/compare/v1.12.1...v1.13.0) (2019-11-23) | ||
@@ -352,3 +413,3 @@ | ||
# [1.12.0](https://github.com/panva/jose/compare/v1.11.0...v1.12.0) (2019-11-05) | ||
## [1.12.0](https://github.com/panva/jose/compare/v1.11.0...v1.12.0) (2019-11-05) | ||
@@ -362,3 +423,3 @@ | ||
# [1.11.0](https://github.com/panva/jose/compare/v1.10.2...v1.11.0) (2019-11-03) | ||
## [1.11.0](https://github.com/panva/jose/compare/v1.10.2...v1.11.0) (2019-11-03) | ||
@@ -390,3 +451,3 @@ | ||
# [1.10.0](https://github.com/panva/jose/compare/v1.9.2...v1.10.0) (2019-10-01) | ||
## [1.10.0](https://github.com/panva/jose/compare/v1.9.2...v1.10.0) (2019-10-01) | ||
@@ -413,3 +474,3 @@ | ||
# [1.9.0](https://github.com/panva/jose/compare/v1.8.0...v1.9.0) (2019-08-24) | ||
## [1.9.0](https://github.com/panva/jose/compare/v1.8.0...v1.9.0) (2019-08-24) | ||
@@ -423,3 +484,3 @@ | ||
# [1.8.0](https://github.com/panva/jose/compare/v1.7.0...v1.8.0) (2019-08-22) | ||
## [1.8.0](https://github.com/panva/jose/compare/v1.7.0...v1.8.0) (2019-08-22) | ||
@@ -433,3 +494,3 @@ | ||
# [1.7.0](https://github.com/panva/jose/compare/v1.6.1...v1.7.0) (2019-08-20) | ||
## [1.7.0](https://github.com/panva/jose/compare/v1.6.1...v1.7.0) (2019-08-20) | ||
@@ -452,3 +513,3 @@ | ||
# [1.6.0](https://github.com/panva/jose/compare/v1.5.2...v1.6.0) (2019-07-27) | ||
## [1.6.0](https://github.com/panva/jose/compare/v1.5.2...v1.6.0) (2019-07-27) | ||
@@ -485,3 +546,3 @@ | ||
# [1.5.0](https://github.com/panva/jose/compare/v1.4.1...v1.5.0) (2019-07-23) | ||
## [1.5.0](https://github.com/panva/jose/compare/v1.4.1...v1.5.0) (2019-07-23) | ||
@@ -504,3 +565,3 @@ | ||
# [1.4.0](https://github.com/panva/jose/compare/v1.3.0...v1.4.0) (2019-07-08) | ||
## [1.4.0](https://github.com/panva/jose/compare/v1.3.0...v1.4.0) (2019-07-08) | ||
@@ -514,3 +575,3 @@ | ||
# [1.3.0](https://github.com/panva/jose/compare/v1.0.2...c51dc28) (2019-06-21) | ||
## [1.3.0](https://github.com/panva/jose/compare/v1.0.2...c51dc28) (2019-06-21) | ||
@@ -569,4 +630,3 @@ | ||
<a name="1.0.0"></a> | ||
# [1.0.0](https://github.com/panva/jose/compare/v0.12.0...v1.0.0) (2019-04-23) | ||
## [1.0.0](https://github.com/panva/jose/compare/v0.12.0...v1.0.0) (2019-04-23) | ||
@@ -597,4 +657,3 @@ | ||
<a name="0.12.0"></a> | ||
# [0.12.0](https://github.com/panva/jose/compare/v0.11.5...v0.12.0) (2019-04-07) | ||
## [0.12.0](https://github.com/panva/jose/compare/v0.11.5...v0.12.0) (2019-04-07) | ||
@@ -614,3 +673,2 @@ | ||
<a name="0.11.5"></a> | ||
## [0.11.5](https://github.com/panva/jose/compare/v0.11.4...v0.11.5) (2019-04-04) | ||
@@ -626,3 +684,2 @@ | ||
<a name="0.11.4"></a> | ||
## [0.11.4](https://github.com/panva/jose/compare/v0.11.3...v0.11.4) (2019-03-28) | ||
@@ -638,3 +695,2 @@ | ||
<a name="0.11.3"></a> | ||
## [0.11.3](https://github.com/panva/jose/compare/v0.11.2...v0.11.3) (2019-03-27) | ||
@@ -654,3 +710,2 @@ | ||
<a name="0.11.2"></a> | ||
## [0.11.2](https://github.com/panva/jose/compare/v0.11.1...v0.11.2) (2019-03-19) | ||
@@ -673,3 +728,3 @@ | ||
# [0.11.0](https://github.com/panva/jose/compare/v0.10.0...v0.11.0) (2019-03-16) | ||
## [0.11.0](https://github.com/panva/jose/compare/v0.10.0...v0.11.0) (2019-03-16) | ||
@@ -690,3 +745,3 @@ | ||
# [0.10.0](https://github.com/panva/jose/compare/v0.9.2...v0.10.0) (2019-03-12) | ||
## [0.10.0](https://github.com/panva/jose/compare/v0.9.2...v0.10.0) (2019-03-12) | ||
@@ -693,0 +748,0 @@ |
@@ -1,6 +0,4 @@ | ||
const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv') | ||
const oids = { | ||
'1 2 840 10045 3 1 7': 'P-256', | ||
'1 3 132 0 10': secp256k1, | ||
'1 3 132 0 10': 'secp256k1', | ||
'1 3 132 0 34': 'P-384', | ||
@@ -7,0 +5,0 @@ '1 3 132 0 35': 'P-521', |
const { improvedDH } = require('../../help/runtime_support') | ||
const { KEYLENGTHS } = require('../../registry') | ||
const { generateSync } = require('../../jwk/generate') | ||
const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv') | ||
@@ -27,3 +26,3 @@ const derive = require('./derive') | ||
JWA.keyManagementDecrypt.set('ECDH-ES', unwrapKey) | ||
JWK.EC.deriveKey['ECDH-ES'] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== secp256k1 | ||
JWK.EC.deriveKey['ECDH-ES'] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== 'secp256k1' | ||
@@ -30,0 +29,0 @@ if (improvedDH) { |
const { improvedDH } = require('../../help/runtime_support') | ||
const { KEYOBJECT } = require('../../help/consts') | ||
const { generateSync } = require('../../jwk/generate') | ||
const { name: secp256k1 } = require('../../jwk/key/secp256k1_crv') | ||
const { ECDH_DERIVE_LENGTHS } = require('../../registry') | ||
@@ -39,3 +38,3 @@ | ||
JWA.keyManagementDecrypt.set(jwaAlg, unwrapKey.bind(undefined, kwUnwrap, derive.bind(undefined, jwaAlg, keylen))) | ||
JWK.EC.deriveKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== secp256k1 | ||
JWK.EC.deriveKey[jwaAlg] = key => (key.use === 'enc' || key.use === undefined) && key.crv !== 'secp256k1' | ||
@@ -42,0 +41,0 @@ if (improvedDH) { |
@@ -8,3 +8,2 @@ const { sign: signOneShot, verify: verifyOneShot, createSign, createVerify, getCurves } = require('crypto') | ||
const { dsaEncodingSupported } = require('../help/runtime_support') | ||
const { name: secp256k1 } = require('../jwk/key/secp256k1_crv') | ||
@@ -44,3 +43,3 @@ let sign, verify | ||
return 'ES256' | ||
case secp256k1: | ||
case 'secp256k1': | ||
return 'ES256K' | ||
@@ -47,0 +46,0 @@ case 'P-384': |
@@ -40,13 +40,22 @@ const { inflateRawSync } = require('zlib') | ||
const validateAlgorithms = (algorithms, option) => { | ||
if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some(s => typeof s !== 'string' || !s))) { | ||
throw new TypeError(`"${option}" option must be an array of non-empty strings`) | ||
} | ||
if (!algorithms) { | ||
return undefined | ||
} | ||
return new Set(algorithms) | ||
} | ||
/* | ||
* @public | ||
*/ | ||
const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, algorithms } = {}) => { | ||
const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms } = {}) => { | ||
key = getKey(key, true) | ||
if (algorithms !== undefined && (!Array.isArray(algorithms) || algorithms.some(s => typeof s !== 'string' || !s))) { | ||
throw new TypeError('"algorithms" option must be an array of non-empty strings') | ||
} else if (algorithms) { | ||
algorithms = new Set(algorithms) | ||
} | ||
keyManagementAlgorithms = validateAlgorithms(keyManagementAlgorithms, 'keyManagementAlgorithms') | ||
contentEncryptionAlgorithms = validateAlgorithms(contentEncryptionAlgorithms, 'contentEncryptionAlgorithms') | ||
@@ -86,6 +95,10 @@ if (!Array.isArray(crit) || crit.some(s => typeof s !== 'string' || !s)) { | ||
if (algorithms && !algorithms.has(alg === 'dir' ? enc : alg)) { | ||
throw new errors.JOSEAlgNotWhitelisted('alg not whitelisted') | ||
if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { | ||
throw new errors.JOSEAlgNotWhitelisted('key management algorithm not whitelisted') | ||
} | ||
if (contentEncryptionAlgorithms && !contentEncryptionAlgorithms.has(enc)) { | ||
throw new errors.JOSEAlgNotWhitelisted('content encryption algorithm not whitelisted') | ||
} | ||
if (key instanceof KeyStore) { | ||
@@ -111,3 +124,8 @@ const keystore = key | ||
try { | ||
return jweDecrypt(true, serialization, jwe, key, { crit, complete, algorithms: algorithms ? [...algorithms] : undefined }) | ||
return jweDecrypt(true, serialization, jwe, key, { | ||
crit, | ||
complete, | ||
contentEncryptionAlgorithms: contentEncryptionAlgorithms ? [...contentEncryptionAlgorithms] : undefined, | ||
keyManagementAlgorithms: keyManagementAlgorithms ? [...keyManagementAlgorithms] : undefined | ||
}) | ||
} catch (err) { | ||
@@ -193,3 +211,8 @@ errs.push(err) | ||
try { | ||
return jweDecrypt(true, 'flattened', { ...root, ...recipient }, key, { crit, complete, algorithms: algorithms ? [...algorithms] : undefined }) | ||
return jweDecrypt(true, 'flattened', { ...root, ...recipient }, key, { | ||
crit, | ||
complete, | ||
contentEncryptionAlgorithms: contentEncryptionAlgorithms ? [...contentEncryptionAlgorithms] : undefined, | ||
keyManagementAlgorithms: keyManagementAlgorithms ? [...keyManagementAlgorithms] : undefined | ||
}) | ||
} catch (err) { | ||
@@ -196,0 +219,0 @@ errs.push(err) |
@@ -21,4 +21,3 @@ const { deflateRawSync } = require('zlib') | ||
class Encrypt { | ||
// TODO: in v2.x swap unprotectedHeader and aad | ||
constructor (cleartext, protectedHeader, unprotectedHeader, aad) { | ||
constructor (cleartext, protectedHeader, aad, unprotectedHeader) { | ||
if (!Buffer.isBuffer(cleartext) && typeof cleartext !== 'string') { | ||
@@ -25,0 +24,0 @@ throw new TypeError('cleartext argument must be a Buffer or a string') |
const Encrypt = require('./encrypt') | ||
const decrypt = require('./decrypt') | ||
// TODO: in v2.x swap unprotectedHeader and aad | ||
const single = (serialization, cleartext, key, protectedHeader, unprotectedHeader, aad) => { | ||
return new Encrypt(cleartext, protectedHeader, unprotectedHeader, aad) | ||
const single = (serialization, cleartext, key, protectedHeader, aad, unprotectedHeader) => { | ||
return new Encrypt(cleartext, protectedHeader, aad, unprotectedHeader) | ||
.recipient(key) | ||
@@ -8,0 +7,0 @@ .encrypt(serialization) |
@@ -1,3 +0,1 @@ | ||
const { deprecate } = require('util') | ||
const { createPublicKey, createPrivateKey, createSecretKey, KeyObject } = require('../help/key_object') | ||
@@ -137,5 +135,1 @@ const base64url = require('../help/base64url') | ||
module.exports = asKey | ||
Object.defineProperty(asKey, 'deprecated', { | ||
value: deprecate((key, parameters) => { return asKey(key, parameters, { calculateMissingRSAPrimes: true }) }, 'JWK.importKey() is deprecated, use JWK.asKey() instead'), | ||
enumerable: false | ||
}) |
@@ -16,7 +16,1 @@ const Key = require('./key/base') | ||
} | ||
/* deprecated */ | ||
Object.defineProperty(module.exports, 'importKey', { | ||
value: importKey.deprecated, | ||
enumerable: false | ||
}) |
@@ -13,3 +13,2 @@ const { generateKeyPairSync, generateKeyPair: async } = require('crypto') | ||
const errors = require('../../errors') | ||
const { name: secp256k1 } = require('./secp256k1_crv') | ||
@@ -66,6 +65,2 @@ const Key = require('./base') | ||
if (crv === secp256k1 && crv !== 'secp256k1') { | ||
crv = 'secp256k1' | ||
} | ||
let privateKey, publicKey | ||
@@ -96,6 +91,2 @@ | ||
if (crv === secp256k1 && crv !== 'secp256k1') { | ||
crv = 'secp256k1' | ||
} | ||
let privateKey, publicKey | ||
@@ -102,0 +93,0 @@ |
@@ -1,2 +0,2 @@ | ||
const { deprecate, inspect } = require('util') | ||
const { inspect } = require('util') | ||
@@ -180,7 +180,2 @@ const isObject = require('../help/is_object') | ||
Object.defineProperty(KeyStore, 'fromJWKS', { | ||
value: deprecate(jwks => asKeyStore(jwks, { calculateMissingRSAPrimes: true }), 'JWKS.KeyStore.fromJWKS() is deprecated, use JWKS.asKeyStore() instead'), | ||
enumerable: false | ||
}) | ||
module.exports = { KeyStore, asKeyStore } |
@@ -21,3 +21,3 @@ const { EOL } = require('os') | ||
*/ | ||
const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], complete = false, algorithms, parse = true, encoding = 'utf8' } = {}) => { | ||
const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], complete = false, algorithms } = {}) => { | ||
key = getKey(key, true) | ||
@@ -117,3 +117,3 @@ | ||
try { | ||
return jwsVerify(true, serialization, jws, key, { crit, complete, encoding, parse, algorithms: algorithms ? [...algorithms] : undefined }) | ||
return jwsVerify(true, serialization, jws, key, { crit, complete, algorithms: algorithms ? [...algorithms] : undefined }) | ||
} catch (err) { | ||
@@ -164,8 +164,6 @@ errs.push(err) | ||
if (!combinedHeader.crit || !combinedHeader.crit.includes('b64') || combinedHeader.b64) { | ||
if (parse) { | ||
payload = decoded ? decoded.payload : base64url.JSON.decode.try(payload, encoding) | ||
} else { | ||
payload = base64url.decodeToBuffer(payload) | ||
} | ||
if (combinedHeader.b64 === false) { | ||
payload = Buffer.from(payload) | ||
} else { | ||
payload = base64url.decodeToBuffer(payload) | ||
} | ||
@@ -188,3 +186,3 @@ | ||
try { | ||
return jwsVerify(false, 'flattened', { ...root, ...recipient }, key, { crit, complete, encoding, parse, algorithms: algorithms ? [...algorithms] : undefined }) | ||
return jwsVerify(false, 'flattened', { ...root, ...recipient }, key, { crit, complete, algorithms: algorithms ? [...algorithms] : undefined }) | ||
} catch (err) { | ||
@@ -191,0 +189,0 @@ errs.push(err) |
@@ -12,3 +12,3 @@ const base64url = require('../help/base64url') | ||
if (length === 5) { | ||
throw new TypeError('JWTs must be decrypted first') | ||
throw new TypeError('encrypted JWTs cannot be decoded') | ||
} | ||
@@ -15,0 +15,0 @@ |
@@ -7,3 +7,2 @@ const decode = require('./decode') | ||
module.exports = { | ||
decode, | ||
sign, | ||
@@ -13,1 +12,6 @@ verify, | ||
} | ||
Object.defineProperty(module.exports, 'decode', { | ||
enumerable: false, | ||
value: decode | ||
}) |
@@ -0,7 +1,168 @@ | ||
const { JWTClaimInvalid } = require('../errors') | ||
const secs = require('../help/secs') | ||
const epoch = require('../help/epoch') | ||
const isObject = require('../help/is_object') | ||
const verify = require('./verify') | ||
const { | ||
isString, | ||
isRequired, | ||
isTimestamp, | ||
isStringOrArrayOfStrings | ||
} = require('./shared_validations') | ||
const isPayloadRequired = isRequired.bind(undefined, JWTClaimInvalid) | ||
const isPayloadString = isString.bind(undefined, JWTClaimInvalid) | ||
const isOptionString = isString.bind(undefined, TypeError) | ||
const defineLazyExportWithWarning = (obj, property, name, definition) => { | ||
Object.defineProperty(obj, property, { | ||
enumerable: true, | ||
configurable: true, | ||
value (...args) { | ||
process.emitWarning( | ||
`The ${name} API implements an IETF draft. Breaking draft implementations are included as minor versions of the jose library, therefore, the ~ semver operator should be used and close attention be payed to library changelog as well as the drafts themselves.`, | ||
'DraftWarning' | ||
) | ||
Object.defineProperty(obj, property, { | ||
enumerable: true, | ||
configurable: true, | ||
value: definition | ||
}) | ||
return obj[property](...args) | ||
} | ||
}) | ||
} | ||
const validateCommonOptions = (options, profile) => { | ||
if (!isObject(options)) { | ||
throw new TypeError('options must be an object') | ||
} | ||
if (!options.issuer) { | ||
throw new TypeError(`"issuer" option is required to validate ${profile}`) | ||
} | ||
if (!options.audience) { | ||
throw new TypeError(`"audience" option is required to validate ${profile}`) | ||
} | ||
} | ||
module.exports = { | ||
IdToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'id_token' }) }, | ||
LogoutToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'logout_token' }) }, | ||
AccessToken: { verify: (token, key, options) => verify(token, key, { ...options, profile: 'at+JWT' }) } | ||
IdToken: { | ||
verify: (token, key, options = {}) => { | ||
validateCommonOptions(options, 'an ID Token') | ||
if ('maxAuthAge' in options) { | ||
isOptionString(options.maxAuthAge, 'options.maxAuthAge') | ||
} | ||
if ('nonce' in options) { | ||
isOptionString(options.nonce, 'options.nonce') | ||
} | ||
const unix = epoch(options.now || new Date()) | ||
const result = verify(token, key, { ...options }) | ||
const payload = options.complete ? result.payload : result | ||
if (Array.isArray(payload.aud) && payload.aud.length > 1) { | ||
isPayloadRequired(payload.azp, '"azp" claim', 'azp') | ||
} | ||
isPayloadRequired(payload.iat, '"iat" claim', 'iat') | ||
isPayloadRequired(payload.sub, '"sub" claim', 'sub') | ||
isPayloadRequired(payload.exp, '"exp" claim', 'exp') | ||
isTimestamp(payload.auth_time, 'auth_time', !!options.maxAuthAge) | ||
isPayloadString(payload.nonce, '"nonce" claim', 'nonce', !!options.nonce) | ||
isPayloadString(payload.acr, '"acr" claim', 'acr') | ||
isStringOrArrayOfStrings(payload.amr, 'amr') | ||
if (options.nonce && payload.nonce !== options.nonce) { | ||
throw new JWTClaimInvalid('unexpected "nonce" claim value', 'nonce', 'check_failed') | ||
} | ||
const tolerance = options.clockTolerance ? secs(options.clockTolerance) : 0 | ||
if (options.maxAuthAge) { | ||
const maxAuthAgeSeconds = secs(options.maxAuthAge) | ||
if (payload.auth_time + maxAuthAgeSeconds < unix - tolerance) { | ||
throw new JWTClaimInvalid('"auth_time" claim timestamp check failed (too much time has elapsed since the last End-User authentication)', 'auth_time', 'check_failed') | ||
} | ||
} | ||
if (Array.isArray(payload.aud) && payload.aud.length > 1 && payload.azp !== options.audience) { | ||
throw new JWTClaimInvalid('unexpected "azp" claim value', 'azp', 'check_failed') | ||
} | ||
return result | ||
} | ||
}, | ||
LogoutToken: {}, | ||
AccessToken: {} | ||
} | ||
defineLazyExportWithWarning(module.exports.LogoutToken, 'verify', 'jose.JWT.LogoutToken.verify', (token, key, options = {}) => { | ||
validateCommonOptions(options, 'a Logout Token') | ||
const result = verify(token, key, { ...options }) | ||
const payload = options.complete ? result.payload : result | ||
isPayloadRequired(payload.iat, '"iat" claim', 'iat') | ||
isPayloadRequired(payload.jti, '"jti" claim', 'jti') | ||
isPayloadString(payload.sid, '"sid" claim', 'sid') | ||
if (!('sid' in payload) && !('sub' in payload)) { | ||
throw new JWTClaimInvalid('either "sid" or "sub" (or both) claims must be present') | ||
} | ||
if ('nonce' in payload) { | ||
throw new JWTClaimInvalid('"nonce" claim is prohibited', 'nonce', 'prohibited') | ||
} | ||
if (!('events' in payload)) { | ||
throw new JWTClaimInvalid('"events" claim is missing', 'events', 'missing') | ||
} | ||
if (!isObject(payload.events)) { | ||
throw new JWTClaimInvalid('"events" claim must be an object', 'events', 'invalid') | ||
} | ||
if (!('http://schemas.openid.net/event/backchannel-logout' in payload.events)) { | ||
throw new JWTClaimInvalid('"http://schemas.openid.net/event/backchannel-logout" member is missing in the "events" claim', 'events', 'invalid') | ||
} | ||
if (!isObject(payload.events['http://schemas.openid.net/event/backchannel-logout'])) { | ||
throw new JWTClaimInvalid('"http://schemas.openid.net/event/backchannel-logout" member in the "events" claim must be an object', 'events', 'invalid') | ||
} | ||
return result | ||
}) | ||
defineLazyExportWithWarning(module.exports.AccessToken, 'verify', 'jose.JWT.AccessToken.verify', (token, key, options = {}) => { | ||
validateCommonOptions(options, 'a JWT Access Token') | ||
isOptionString(options.maxAuthAge, 'options.maxAuthAge') | ||
const unix = epoch(options.now || new Date()) | ||
const typ = 'at+JWT' | ||
const result = verify(token, key, { ...options, typ }) | ||
const payload = options.complete ? result.payload : result | ||
isPayloadRequired(payload.iat, '"iat" claim', 'iat') | ||
isPayloadRequired(payload.exp, '"exp" claim', 'exp') | ||
isPayloadRequired(payload.sub, '"sub" claim', 'sub') | ||
isPayloadRequired(payload.jti, '"jti" claim', 'jti') | ||
isPayloadString(payload.client_id, '"client_id" claim', 'client_id', true) | ||
isTimestamp(payload.auth_time, 'auth_time', !!options.maxAuthAge) | ||
isPayloadString(payload.acr, '"acr" claim', 'acr') | ||
isStringOrArrayOfStrings(payload.amr, 'amr') | ||
const tolerance = options.clockTolerance ? secs(options.clockTolerance) : 0 | ||
if (options.maxAuthAge) { | ||
const maxAuthAgeSeconds = secs(options.maxAuthAge) | ||
if (payload.auth_time + maxAuthAgeSeconds < unix - tolerance) { | ||
throw new JWTClaimInvalid('"auth_time" claim timestamp check failed (too much time has elapsed since the last End-User authentication)', 'auth_time', 'check_failed') | ||
} | ||
} | ||
return result | ||
}) |
@@ -0,8 +1,14 @@ | ||
const { JWTClaimInvalid } = require('../errors') | ||
const isNotString = val => typeof val !== 'string' || val.length === 0 | ||
module.exports.isNotString = isNotString | ||
module.exports.isString = function isString (Err, value, label, claim, required = false) { | ||
if (required && value === undefined) { | ||
const isNotArrayOfStrings = val => !Array.isArray(val) || val.length === 0 || val.some(isNotString) | ||
const isRequired = (Err, value, label, claim) => { | ||
if (value === undefined) { | ||
throw new Err(`${label} is missing`, claim, 'missing') | ||
} | ||
} | ||
const isString = (Err, value, label, claim, required = false) => { | ||
if (required) { | ||
isRequired(Err, value, label, claim) | ||
} | ||
@@ -13,1 +19,28 @@ if (value !== undefined && isNotString(value)) { | ||
} | ||
const isTimestamp = (value, label, required = false) => { | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`, label, 'missing') | ||
} | ||
if (value !== undefined && (typeof value !== 'number')) { | ||
throw new JWTClaimInvalid(`"${label}" claim must be a JSON numeric value`, label, 'invalid') | ||
} | ||
} | ||
const isStringOrArrayOfStrings = (value, label, required = false) => { | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`, label, 'missing') | ||
} | ||
if (value !== undefined && (isNotString(value) && isNotArrayOfStrings(value))) { | ||
throw new JWTClaimInvalid(`"${label}" claim must be a string or array of strings`, label, 'invalid') | ||
} | ||
} | ||
module.exports = { | ||
isNotArrayOfStrings, | ||
isRequired, | ||
isNotString, | ||
isString, | ||
isTimestamp, | ||
isStringOrArrayOfStrings | ||
} |
@@ -39,3 +39,2 @@ const isObject = require('../help/is_object') | ||
isString(options.jti, 'options.jti') | ||
isString(options.nonce, 'options.nonce') | ||
@@ -54,7 +53,7 @@ if (options.now !== undefined && (!(options.now instanceof Date) || !options.now.getTime())) { | ||
algorithm, audience, expiresIn, header = {}, iat = true, | ||
issuer, jti, kid = true, nonce, notBefore, subject, now | ||
issuer, jti, kid = true, notBefore, subject, now | ||
} = options | ||
validateOptions({ | ||
algorithm, audience, expiresIn, header, iat, issuer, jti, kid, nonce, notBefore, now, subject | ||
algorithm, audience, expiresIn, header, iat, issuer, jti, kid, notBefore, now, subject | ||
}) | ||
@@ -78,3 +77,2 @@ | ||
iat: iat ? unix : payload.iat, | ||
nonce: nonce || payload.nonce, | ||
exp: expiresIn ? unix + secs(expiresIn) : payload.exp, | ||
@@ -81,0 +79,0 @@ nbf: notBefore ? unix + secs(notBefore) : payload.nbf |
@@ -8,3 +8,9 @@ const isObject = require('../help/is_object') | ||
const { isString, isNotString } = require('./shared_validations') | ||
const { | ||
isString, | ||
isNotString, | ||
isNotArrayOfStrings, | ||
isTimestamp, | ||
isStringOrArrayOfStrings | ||
} = require('./shared_validations') | ||
const decode = require('./decode') | ||
@@ -15,27 +21,2 @@ | ||
const IDTOKEN = 'id_token' | ||
const LOGOUTTOKEN = 'logout_token' | ||
const ATJWT = 'at+JWT' | ||
const isTimestamp = (value, label, required = false) => { | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`, label, 'missing') | ||
} | ||
if (value !== undefined && (typeof value !== 'number')) { | ||
throw new JWTClaimInvalid(`"${label}" claim must be a JSON numeric value`, label, 'invalid') | ||
} | ||
} | ||
const isStringOrArrayOfStrings = (value, label, required = false) => { | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`, label, 'missing') | ||
} | ||
if (value !== undefined && (isNotString(value) && isNotArrayOfStrings(value))) { | ||
throw new JWTClaimInvalid(`"${label}" claim must be a string or array of strings`, label, 'invalid') | ||
} | ||
} | ||
const isNotArrayOfStrings = val => !Array.isArray(val) || val.length === 0 || val.some(isNotString) | ||
const normalizeTyp = (value) => value.toLowerCase().replace(/^application\//, '') | ||
@@ -45,7 +26,5 @@ | ||
algorithms, audience, clockTolerance, complete = false, crit, ignoreExp = false, | ||
ignoreIat = false, ignoreNbf = false, issuer, jti, maxAuthAge, maxTokenAge, nonce, now = new Date(), | ||
profile, subject, typ | ||
ignoreIat = false, ignoreNbf = false, issuer, jti, maxTokenAge, now = new Date(), | ||
subject, typ | ||
}) => { | ||
isOptionString(profile, 'options.profile') | ||
if (typeof complete !== 'boolean') { | ||
@@ -69,3 +48,2 @@ throw new TypeError('options.complete must be a boolean') | ||
isOptionString(subject, 'options.subject') | ||
isOptionString(maxAuthAge, 'options.maxAuthAge') | ||
isOptionString(jti, 'options.jti') | ||
@@ -87,4 +65,2 @@ isOptionString(clockTolerance, 'options.clockTolerance') | ||
isOptionString(nonce, 'options.nonce') | ||
if (!(now instanceof Date) || !now.getTime()) { | ||
@@ -102,41 +78,2 @@ throw new TypeError('options.now must be a valid Date object') | ||
switch (profile) { | ||
case IDTOKEN: | ||
if (!issuer) { | ||
throw new TypeError('"issuer" option is required to validate an ID Token') | ||
} | ||
if (!audience) { | ||
throw new TypeError('"audience" option is required to validate an ID Token') | ||
} | ||
break | ||
case ATJWT: | ||
if (!issuer) { | ||
throw new TypeError('"issuer" option is required to validate a JWT Access Token') | ||
} | ||
if (!audience) { | ||
throw new TypeError('"audience" option is required to validate a JWT Access Token') | ||
} | ||
typ = ATJWT | ||
break | ||
case LOGOUTTOKEN: | ||
if (!issuer) { | ||
throw new TypeError('"issuer" option is required to validate a Logout Token') | ||
} | ||
if (!audience) { | ||
throw new TypeError('"audience" option is required to validate a Logout Token') | ||
} | ||
break | ||
case undefined: | ||
break | ||
default: | ||
throw new TypeError(`unsupported options.profile value "${profile}"`) | ||
} | ||
return { | ||
@@ -153,7 +90,4 @@ algorithms, | ||
jti, | ||
maxAuthAge, | ||
maxTokenAge, | ||
nonce, | ||
now, | ||
profile, | ||
subject, | ||
@@ -164,53 +98,16 @@ typ | ||
const validateTypes = ({ header, payload }, profile, options) => { | ||
const validateTypes = ({ header, payload }, options) => { | ||
isPayloadString(header.alg, '"alg" header parameter', 'alg', true) | ||
isTimestamp(payload.iat, 'iat', profile === IDTOKEN || profile === LOGOUTTOKEN || profile === ATJWT || !!options.maxTokenAge) | ||
isTimestamp(payload.exp, 'exp', profile === IDTOKEN || profile === ATJWT) | ||
isTimestamp(payload.auth_time, 'auth_time', !!options.maxAuthAge) | ||
isTimestamp(payload.iat, 'iat', !!options.maxTokenAge) | ||
isTimestamp(payload.exp, 'exp') | ||
isTimestamp(payload.nbf, 'nbf') | ||
isPayloadString(payload.jti, '"jti" claim', 'jti', profile === LOGOUTTOKEN || profile === ATJWT || !!options.jti) | ||
isPayloadString(payload.acr, '"acr" claim', 'acr') | ||
isPayloadString(payload.nonce, '"nonce" claim', 'nonce', !!options.nonce) | ||
isPayloadString(payload.jti, '"jti" claim', 'jti', !!options.jti) | ||
isStringOrArrayOfStrings(payload.iss, 'iss', !!options.issuer) | ||
isPayloadString(payload.sub, '"sub" claim', 'sub', profile === IDTOKEN || profile === ATJWT || !!options.subject) | ||
isPayloadString(payload.sub, '"sub" claim', 'sub', !!options.subject) | ||
isStringOrArrayOfStrings(payload.aud, 'aud', !!options.audience) | ||
isPayloadString(payload.azp, '"azp" claim', 'azp', profile === IDTOKEN && Array.isArray(payload.aud) && payload.aud.length > 1) | ||
isStringOrArrayOfStrings(payload.amr, 'amr') | ||
isPayloadString(header.typ, '"typ" header parameter', 'typ', !!options.typ) | ||
if (profile === ATJWT) { | ||
isPayloadString(payload.client_id, '"client_id" claim', 'client_id', true) | ||
} | ||
if (profile === LOGOUTTOKEN) { | ||
isPayloadString(payload.sid, '"sid" claim', 'sid') | ||
if (!('sid' in payload) && !('sub' in payload)) { | ||
throw new JWTClaimInvalid('either "sid" or "sub" (or both) claims must be present') | ||
} | ||
if ('nonce' in payload) { | ||
throw new JWTClaimInvalid('"nonce" claim is prohibited', 'nonce', 'prohibited') | ||
} | ||
if (!('events' in payload)) { | ||
throw new JWTClaimInvalid('"events" claim is missing', 'events', 'missing') | ||
} | ||
if (!isObject(payload.events)) { | ||
throw new JWTClaimInvalid('"events" claim must be an object', 'events', 'invalid') | ||
} | ||
if (!('http://schemas.openid.net/event/backchannel-logout' in payload.events)) { | ||
throw new JWTClaimInvalid('"http://schemas.openid.net/event/backchannel-logout" member is missing in the "events" claim', 'events', 'invalid') | ||
} | ||
if (!isObject(payload.events['http://schemas.openid.net/event/backchannel-logout'])) { | ||
throw new JWTClaimInvalid('"http://schemas.openid.net/event/backchannel-logout" member in the "events" claim must be an object', 'events', 'invalid') | ||
} | ||
} | ||
} | ||
const checkAudiencePresence = (audPayload, audOption, profile) => { | ||
const checkAudiencePresence = (audPayload, audOption) => { | ||
if (typeof audPayload === 'string') { | ||
@@ -233,3 +130,3 @@ return audOption.includes(audPayload) | ||
algorithms, audience, clockTolerance, complete, crit, ignoreExp, ignoreIat, ignoreNbf, issuer, | ||
jti, maxAuthAge, maxTokenAge, nonce, now, profile, subject, typ | ||
jti, maxTokenAge, now, subject, typ | ||
} = options = validateOptions(options) | ||
@@ -248,3 +145,3 @@ | ||
const unix = epoch(now) | ||
validateTypes(decoded, profile, options) | ||
validateTypes(decoded, options) | ||
@@ -255,6 +152,2 @@ if (issuer && (typeof decoded.payload.iss !== 'string' || !(typeof issuer === 'string' ? [issuer] : issuer).includes(decoded.payload.iss))) { | ||
if (nonce && decoded.payload.nonce !== nonce) { | ||
throw new JWTClaimInvalid('unexpected "nonce" claim value', 'nonce', 'check_failed') | ||
} | ||
if (subject && decoded.payload.sub !== subject) { | ||
@@ -268,3 +161,3 @@ throw new JWTClaimInvalid('unexpected "sub" claim value', 'sub', 'check_failed') | ||
if (audience && !checkAudiencePresence(decoded.payload.aud, typeof audience === 'string' ? [audience] : audience, profile)) { | ||
if (audience && !checkAudiencePresence(decoded.payload.aud, typeof audience === 'string' ? [audience] : audience)) { | ||
throw new JWTClaimInvalid('unexpected "aud" claim value', 'aud', 'check_failed') | ||
@@ -279,9 +172,2 @@ } | ||
if (maxAuthAge) { | ||
const maxAuthAgeSeconds = secs(maxAuthAge) | ||
if (decoded.payload.auth_time + maxAuthAgeSeconds < unix - tolerance) { | ||
throw new JWTClaimInvalid('"auth_time" claim timestamp check failed (too much time has elapsed since the last End-User authentication)', 'auth_time', 'check_failed') | ||
} | ||
} | ||
if (!ignoreIat && !('exp' in decoded.payload) && 'iat' in decoded.payload && decoded.payload.iat > unix + tolerance) { | ||
@@ -312,7 +198,3 @@ throw new JWTClaimInvalid('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed') | ||
if (profile === IDTOKEN && Array.isArray(decoded.payload.aud) && decoded.payload.aud.length > 1 && decoded.payload.azp !== audience) { | ||
throw new JWTClaimInvalid('unexpected "azp" claim value', 'azp', 'check_failed') | ||
} | ||
return complete ? decoded : decoded.payload | ||
} |
const { getCurves } = require('crypto') | ||
const { name: secp256k1 } = require('../jwk/key/secp256k1_crv') | ||
const curves = new Set() | ||
@@ -12,3 +10,3 @@ | ||
if (getCurves().includes('secp256k1')) { | ||
curves.add(secp256k1) | ||
curves.add('secp256k1') | ||
} | ||
@@ -15,0 +13,0 @@ |
{ | ||
"name": "jose", | ||
"version": "1.28.0", | ||
"version": "2.0.0", | ||
"description": "JSON Web Almost Everything - JWA, JWS, JWE, JWK, JWT, JWKS for Node.js with minimal dependencies", | ||
@@ -48,2 +48,8 @@ "keywords": [ | ||
"author": "Filip Skokan <panva.ip@gmail.com>", | ||
"exports": { | ||
"import": "./lib/index.mjs", | ||
"require": "./lib/index.js" | ||
}, | ||
"main": "lib/index.js", | ||
"types": "types/index.d.ts", | ||
"files": [ | ||
@@ -54,9 +60,7 @@ "lib/**/*.js", | ||
], | ||
"main": "lib/index.js", | ||
"types": "types/index.d.ts", | ||
"scripts": { | ||
"coverage": "c8 ava", | ||
"lint": "standard", | ||
"lint-fix": "standard --fix", | ||
"lint-ts": "npx typescript@~3.6.0 --build types", | ||
"lint-fix": "standard --fix", | ||
"test": "ava", | ||
@@ -95,7 +99,47 @@ "watch": "ava --watch" | ||
"engines": { | ||
"node": ">=10.13.0" | ||
"node": ">=10.13.0 < 13 || >=13.7.0" | ||
}, | ||
"standard": { | ||
"parser": "babel-eslint" | ||
}, | ||
"standard-version": { | ||
"scripts": { | ||
"postchangelog": "sed -i '' -e 's/### \\[/## [/g' CHANGELOG.md" | ||
}, | ||
"types": [ | ||
{ | ||
"type": "feat", | ||
"section": "Features" | ||
}, | ||
{ | ||
"type": "fix", | ||
"section": "Bug Fixes" | ||
}, | ||
{ | ||
"type": "chore", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "docs", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "style", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "refactor", | ||
"section": "Refactor", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "perf", | ||
"hidden": true | ||
}, | ||
{ | ||
"type": "test", | ||
"hidden": true | ||
} | ||
] | ||
} | ||
} |
@@ -19,3 +19,3 @@ # jose | ||
- CFRG Elliptic Curve ECDH and Signatures - [RFC8037][spec-okp] | ||
- secp256k1 EC Key curve support - [JOSE Registrations for WebAuthn Algorithms][draft-secp256k1] | ||
- secp256k1 EC Key curve support - [JOSE Registrations for WebAuthn Algorithms][spec-secp256k1] | ||
@@ -28,10 +28,6 @@ The test suite utilizes examples defined in [RFC7520][spec-cookbook] to confirm its JOSE | ||
- Generic JWT | ||
- OIDC ID Token (`id_token`) - [OpenID Connect Core 1.0][spec-oidc-id_token] | ||
- (draft 04) OIDC Logout Token (`logout_token`) - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token] | ||
- (draft 06) OAuth 2.0 JWT Access Tokens (`at+JWT`) - [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt] | ||
- OIDC ID Token - [OpenID Connect Core 1.0][spec-oidc-id_token] | ||
- (draft 04) OIDC Logout Token - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token] | ||
- (draft 06) OAuth 2.0 JWT Access Tokens - [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt] | ||
Draft profiles are updated as minor versions of the library, therefore, since they may have breaking | ||
changes use the `~` semver operator when using these and pay close attention to changelog and the | ||
drafts themselves. | ||
## Sponsor | ||
@@ -147,4 +143,4 @@ | ||
ID Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an | ||
ID Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the | ||
`JWT.IdToken.verify` shorthand to make sure what you're accepting is really an ID Token meant to | ||
ID Token and it is pretty easy to omit some, use the | ||
`JWT.IdToken.verify` API to make sure what you're accepting is really an ID Token meant to | ||
your Client. This will then perform all doable validations given the input. See the | ||
@@ -181,4 +177,4 @@ [documentation][documentation-jwt] for more. | ||
to be accepted as an Access Token according to the [specification][draft-ietf-oauth-access-token-jwt] | ||
and it is pretty easy to omit some. Use the `profile` option of `JWT.verify` or the | ||
`JWT.AccessToken.verify` shorthand to make sure what you're accepting is really a JWT Access Token | ||
and it is pretty easy to omit some. Use the | ||
`JWT.AccessToken.verify` API to make sure what you're accepting is really a JWT Access Token | ||
meant for your Resource Server. This will then perform all doable validations given the input. See | ||
@@ -209,4 +205,4 @@ the [documentation][documentation-jwt] for more. | ||
Logout Token is a JWT, but profiled, there are additional requirements to a JWT to be accepted as an | ||
Logout Token and it is pretty easy to omit some, use the `profile` option of `JWT.verify` or the | ||
`JWT.LogoutToken.verify` to make sure what you're accepting is really an Logout Token meant to your | ||
Logout Token and it is pretty easy to omit some, use the | ||
`JWT.LogoutToken.verify` API to make sure what you're accepting is really an Logout Token meant to your | ||
Client. This will then perform all doable validations given the input. See the | ||
@@ -317,9 +313,10 @@ [documentation][documentation-jwt] for more. | ||
| JWT profile validation | Supported | Stable profile | profile option value | | ||
| JWT profile validation | Supported | Stable profile | | | ||
| -- | -- | -- | -- | | ||
| ID Token - [OpenID Connect Core 1.0][spec-oidc-id_token] | ✓ | ✓ | `id_token` | | ||
| JWT Access Tokens - [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt] | ✓ | ✕<sup>5</sup> | `at+JWT` | | ||
| Logout Token - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token] | ✓ | ✕<sup>5</sup> | `logout_token` | | ||
| JWT Access Tokens - [JWT Profile for OAuth 2.0 Access Tokens][draft-ietf-oauth-access-token-jwt] | ✓ | ✕<sup>5</sup> | see [`JWT.AccessToken.verify`](/docs/README.md#jwtaccesstokenverifytoken-keyorstore-options) | | ||
| ID Token - [OpenID Connect Core 1.0][spec-oidc-id_token] | ✓ | ✓ | see [`JWT.IdToken.verify`](/docs/README.md#jwtidtokenverifytoken-keyorstore-options) | | ||
| Logout Token - [OpenID Connect Back-Channel Logout 1.0][spec-oidc-logout_token] | ✓ | ✕<sup>5</sup> | see [`JWT.LogoutToken.verify`](/docs/README.md#jwtlogouttokenverifytoken-keyorstore-options) | | ||
| JARM - [JWT Secured Authorization Response Mode for OAuth 2.0][draft-jarm] | ◯ ||| | ||
| [JWT Response for OAuth Token Introspection][draft-jwtintrospection] | ◯ ||| | ||
| [OAuth 2.0 DPoP][draft-dpop] | ◯ ||| | ||
@@ -409,6 +406,7 @@ Legend: | ||
[spec-okp]: https://tools.ietf.org/html/rfc8037 | ||
[draft-secp256k1]: https://tools.ietf.org/html/draft-ietf-cose-webauthn-algorithms-04 | ||
[spec-secp256k1]: https://tools.ietf.org/html/rfc8812 | ||
[draft-ietf-oauth-access-token-jwt]: https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-06 | ||
[draft-jarm]: https://openid.net/specs/openid-financial-api-jarm.html | ||
[draft-jwtintrospection]: https://tools.ietf.org/html/draft-ietf-oauth-jwt-introspection-response | ||
[draft-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop | ||
[spec-thumbprint]: https://tools.ietf.org/html/rfc7638 | ||
@@ -415,0 +413,0 @@ [spec-oidc-id_token]: https://openid.net/specs/openid-connect-core-1_0.html#IDToken |
@@ -25,3 +25,2 @@ /// <reference types="node" /> | ||
export type keyObjectTypes = asymmetricKeyObjectTypes | 'secret'; | ||
export type JWTProfiles = 'id_token' | 'at+JWT' | 'logout_token'; | ||
export type KeyInput = PrivateKeyInput | PublicKeyInput | string | Buffer; | ||
@@ -315,3 +314,3 @@ export type ProduceKeyInput = JWK.Key | KeyObject | KeyInput | JWKOctKey | JWKRSAKey | JWKECKey | JWKOKPKey; | ||
interface JWSJSON { | ||
payload: string; | ||
payload: string | Buffer; | ||
} | ||
@@ -347,6 +346,4 @@ | ||
interface VerifyOptions<komplet = false, parse = true> { | ||
complete?: komplet; | ||
parse?: parse; | ||
encoding?: BufferEncoding; | ||
interface VerifyOptions { | ||
complete?: boolean; | ||
crit?: string[]; | ||
@@ -356,5 +353,5 @@ algorithms?: string[]; | ||
interface completeVerification<T, T2> { | ||
payload: T; | ||
key: T2; | ||
interface completeVerification<T = JWK.Key> { | ||
payload: Buffer; | ||
key: T; | ||
protected?: object; | ||
@@ -364,8 +361,5 @@ header?: object; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options?: VerifyOptions): string | object; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options?: VerifyOptions<false, false>): Buffer; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInput | EmbeddedVerifyKeys, options?: VerifyOptions<true>): completeVerification<string | object, JWK.Key>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInput | EmbeddedVerifyKeys, options?: VerifyOptions<true, false>): completeVerification<Buffer, JWK.Key>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: NoneKey, options?: VerifyOptions<true>): completeVerification<string | object, NoneKey>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: NoneKey, options?: VerifyOptions<true, false>): completeVerification<Buffer, NoneKey>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions & { complete: true }): completeVerification<JWK.Key>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: NoneKey, options: VerifyOptions & { complete: true }): completeVerification<NoneKey>; | ||
function verify(jws: string | FlattenedJWS | GeneralJWS, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options?: VerifyOptions): Buffer; | ||
} | ||
@@ -395,3 +389,3 @@ | ||
class Encrypt { | ||
constructor(cleartext: string | Buffer, protected?: object, unprotected?: object, aad?: string); | ||
constructor(cleartext: string | Buffer, protected?: object, aad?: string, unprotected?: object); | ||
@@ -407,10 +401,11 @@ recipient(key: ProduceKeyInput, header?: object): void; | ||
namespace encrypt { | ||
function flattened(payload: string | Buffer, key: ProduceKeyInput, protected?: object, header?: object, aad?: string): FlattenedJWE; | ||
function general(payload: string | Buffer, key: ProduceKeyInput, protected?: object, header?: object, aad?: string): GeneralJWE; | ||
function flattened(payload: string | Buffer, key: ProduceKeyInput, protected?: object, aad?: string, header?: object): FlattenedJWE; | ||
function general(payload: string | Buffer, key: ProduceKeyInput, protected?: object, aad?: string, header?: object): GeneralJWE; | ||
} | ||
interface DecryptOptions<komplet> { | ||
complete?: komplet; | ||
interface DecryptOptions { | ||
complete?: boolean; | ||
crit?: string[]; | ||
algorithms?: string[]; | ||
contentEncryptionAlgorithms?: string[]; | ||
keyManagementAlgorithms?: string[]; | ||
} | ||
@@ -428,4 +423,4 @@ | ||
function decrypt(jwe: string | FlattenedJWE | GeneralJWE, key: ConsumeKeyInput, options?: DecryptOptions<false>): Buffer; | ||
function decrypt(jwe: string | FlattenedJWE | GeneralJWE, key: ConsumeKeyInput, options?: DecryptOptions<true>): completeDecrypt; | ||
function decrypt(jwe: string | FlattenedJWE | GeneralJWE, key: ConsumeKeyInput, options: DecryptOptions & { complete: true }): completeDecrypt; | ||
function decrypt(jwe: string | FlattenedJWE | GeneralJWE, key: ConsumeKeyInput, options?: DecryptOptions): Buffer; | ||
} | ||
@@ -441,11 +436,15 @@ | ||
interface DecodeOptions<komplet> { | ||
complete?: komplet; | ||
interface DecodeOptions { | ||
complete?: boolean; | ||
} | ||
function decode(jwt: string, options?: DecodeOptions<false>): object; | ||
function decode(jwt: string, options?: DecodeOptions<true>): completeResult<undefined>; | ||
/** | ||
* Decodes the JWT **without verifying the token**. For JWT verification/validation use | ||
* `jose.JWT.verify`. | ||
*/ | ||
function decode(jwt: string, options: DecodeOptions & { complete: true }): completeResult<undefined>; | ||
function decode(jwt: string, options?: DecodeOptions): object; | ||
interface VerifyOptions<komplet> { | ||
complete?: komplet; | ||
interface VerifyOptions { | ||
complete?: boolean; | ||
ignoreExp?: boolean; | ||
@@ -457,3 +456,2 @@ ignoreNbf?: boolean; | ||
issuer?: string | string[]; | ||
maxAuthAge?: string; | ||
jti?: string; | ||
@@ -463,12 +461,10 @@ clockTolerance?: string; | ||
algorithms?: string[]; | ||
nonce?: string; | ||
typ?: string; | ||
now?: Date; | ||
crit?: string[]; | ||
profile?: JWTProfiles; | ||
} | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options?: VerifyOptions<false>): object; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options?: VerifyOptions<true>): completeResult; | ||
function verify(jwt: string, key: NoneKey, options?: VerifyOptions<true>): completeResult<NoneKey>; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions & { complete: true }): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions & { complete: true }): completeResult; | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options?: VerifyOptions): object; | ||
@@ -486,3 +482,2 @@ interface SignOptions { | ||
jti?: string; | ||
nonce?: string; | ||
now?: Date; | ||
@@ -493,24 +488,34 @@ } | ||
interface VerifyProfileOptions<profile> { | ||
interface ProfiledVerifyOptions { | ||
issuer: string | string[]; | ||
audience: string | string[]; | ||
profile?: profile; | ||
} | ||
interface IdTokenVerifyOptions extends ProfiledVerifyOptions { | ||
nonce?: string; | ||
maxAuthAge?: string; | ||
} | ||
interface AccessTokenVerifyOptions extends ProfiledVerifyOptions { | ||
maxAuthAge?: string; | ||
} | ||
interface LogoutTokenVerifyOptions extends ProfiledVerifyOptions {} | ||
namespace IdToken { | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions<false> & VerifyProfileOptions<'id_token'>): object; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions<true> & VerifyProfileOptions<'id_token'>): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions<true> & VerifyProfileOptions<'id_token'>): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions & { complete: true } & IdTokenVerifyOptions): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions & { complete: true } & IdTokenVerifyOptions): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions & IdTokenVerifyOptions): object; | ||
} | ||
namespace LogoutToken { | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions<false> & VerifyProfileOptions<'logout_token'>): object; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions<true> & VerifyProfileOptions<'logout_token'>): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions<true> & VerifyProfileOptions<'logout_token'>): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions & { complete: true } & LogoutTokenVerifyOptions): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions & { complete: true } & LogoutTokenVerifyOptions): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions & LogoutTokenVerifyOptions): object; | ||
} | ||
namespace AccessToken { | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions<false> & VerifyProfileOptions<'at+JWT'>): object; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions<true> & VerifyProfileOptions<'at+JWT'>): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions<true> & VerifyProfileOptions<'at+JWT'>): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInput | EmbeddedVerifyKeys, options: VerifyOptions & { complete: true } & AccessTokenVerifyOptions): completeResult; | ||
function verify(jwt: string, key: NoneKey, options: VerifyOptions & { complete: true } & AccessTokenVerifyOptions): completeResult<NoneKey>; | ||
function verify(jwt: string, key: ConsumeKeyInputWithNone | EmbeddedVerifyKeys, options: VerifyOptions & AccessTokenVerifyOptions): object; | ||
} | ||
@@ -517,0 +522,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
237647
4904
91
411