Comparing version 4.0.5 to 4.0.6
{ | ||
"name": "fast-jwt", | ||
"version": "4.0.5", | ||
"version": "4.0.6", | ||
"description": "Fast JSON Web Token implementation", | ||
@@ -43,5 +43,5 @@ "author": "NearForm Ltd", | ||
"lint": "eslint src/**/*.js test/**/*.js src/**/*.ts test/**/*.ts", | ||
"test": "tap --reporter=spec --coverage-report=html --coverage-report=text --100 --no-browser test/*.spec.js test/**/*.spec.js && tsd", | ||
"test:ci": "npm run lint && tap --no-color --reporter=spec --coverage-report=json --coverage-report=text --100 test/*.spec.js test/**/*.spec.js && tsd", | ||
"test:watch": "tap --watch --reporter=spec --coverage-report=html --coverage-report=text --no-browser test/*.spec.js test/**/*.spec.js", | ||
"test": "node --test --experimental-test-coverage && tsd", | ||
"test:ci": "npm run lint && npm run test", | ||
"test:watch": "node --test --watch --experimental-test-coverage", | ||
"test:generate-keys": "node benchmarks/keys/generate-keys.js", | ||
@@ -66,3 +66,3 @@ "test:generate-tokens": "node benchmarks/keys/generate-tokens.js", | ||
"@typescript-eslint/parser": "^5.49.0", | ||
"cronometro": "^3.0.2", | ||
"cronometro": "^4.0.0", | ||
"eslint": "^8.33.0", | ||
@@ -73,7 +73,6 @@ "eslint-config-standard": "^17.0.0", | ||
"eslint-plugin-promise": "^6.1.1", | ||
"fastify": "^4.12.0", | ||
"fastify": "^5.0.0", | ||
"jose": "^2.0.6", | ||
"jsonwebtoken": "^9.0.0", | ||
"prettier": "^3.0.0", | ||
"tap": "^16.3.4", | ||
"tsd": "^0.31.0", | ||
@@ -83,3 +82,3 @@ "typescript": "^5.0.2" | ||
"engines": { | ||
"node": ">=16" | ||
"node": ">=20" | ||
}, | ||
@@ -86,0 +85,0 @@ "tsd": { |
@@ -179,2 +179,4 @@ # fast-jwt | ||
- `cacheKeyBuilder`: The function that will be used to create the [cache's key](#caching) for each token. To mitigate the risk of leaking sensitive information and generate collisions, [a hashing function](./src/utils.js) is used by default. | ||
The verifier is a function which accepts a token (as Buffer or string) and returns the payload or the sections of the token. | ||
@@ -278,2 +280,6 @@ | ||
The default `cacheKeyBuilder` is a function that hashes the token. This provides a good level of protection against sensitive information leaks, but it also has a significant performance impact (almost 10x slower, as it's a CPU bound operation). If you are using caching and you are not concerned about potential information leaks you can use the identity function as `cacheKeyBuilder` to improve them. | ||
For a detailed discussion about it, take a look at [this issue](https://github.com/nearform/fast-jwt/issues/503). | ||
> **_Note:_** Errors are not cached by default, to change this behaviour use the `errorCacheTTL` option. | ||
@@ -280,0 +286,0 @@ |
@@ -18,3 +18,3 @@ 'use strict' | ||
} = require('node:crypto') | ||
let { sign: directSign, verify: directVerify } = require('node:crypto') | ||
const { sign: directSign, verify: directVerify } = require('node:crypto') | ||
const { joseToDer, derToJose } = require('ecdsa-sig-formatter') | ||
@@ -24,4 +24,2 @@ const Cache = require('mnemonist/lru-cache') | ||
const useNewCrypto = typeof directSign === 'function' | ||
const base64UrlMatcher = /[=+/]/g | ||
@@ -47,15 +45,2 @@ const encoderMap = { '=': '', '+': '-', '/': '_' } | ||
/* istanbul ignore next */ | ||
if (!useNewCrypto) { | ||
directSign = function (alg, data, options) { | ||
if (typeof alg === 'undefined') { | ||
throw new TokenError(TokenError.codes.signError, 'EdDSA algorithms are not supported by your Node.js version.') | ||
} | ||
return createSign(alg) | ||
.update(data) | ||
.sign(options) | ||
} | ||
} | ||
const PrivateKey = asn.define('PrivateKey', function () { | ||
@@ -357,3 +342,2 @@ this.seq().obj( | ||
module.exports = { | ||
useNewCrypto, | ||
base64UrlMatcher, | ||
@@ -360,0 +344,0 @@ base64UrlReplacer, |
@@ -6,3 +6,2 @@ 'use strict' | ||
base64UrlReplacer, | ||
useNewCrypto, | ||
hsAlgorithms, | ||
@@ -49,9 +48,3 @@ esAlgorithms, | ||
// Only on Node 12 - Create a key object | ||
/* istanbul ignore next */ | ||
if (useNewCrypto) { | ||
key = algorithm[0] === 'H' ? createSecretKey(key) : createPrivateKey(key) | ||
} | ||
return key | ||
return algorithm[0] === 'H' ? createSecretKey(key) : createPrivateKey(key) | ||
} | ||
@@ -305,3 +298,3 @@ | ||
// Return the signer | ||
const context = { | ||
return sign.bind(null, { | ||
key, | ||
@@ -319,5 +312,3 @@ algorithm, | ||
fixedPayload | ||
} | ||
return sign.bind(null, context) | ||
}) | ||
} |
@@ -6,3 +6,3 @@ 'use strict' | ||
const { useNewCrypto, hsAlgorithms, verifySignature, detectPublicKeyAlgorithms } = require('./crypto') | ||
const { hsAlgorithms, verifySignature, detectPublicKeyAlgorithms } = require('./crypto') | ||
const createDecoder = require('./decoder') | ||
@@ -19,19 +19,13 @@ const { TokenError } = require('./error') | ||
function checkAreCompatibleAlgorithms(expected, actual) { | ||
let valid = false | ||
for (const expectedAlg of expected) { | ||
valid = actual.indexOf(expectedAlg) !== -1 | ||
// if at least one of the expected algorithms is compatible we're done | ||
if (valid) { | ||
break | ||
if (actual.includes(expectedAlg)) { | ||
return | ||
} | ||
} | ||
if (!valid) { | ||
throw new TokenError( | ||
TokenError.codes.invalidKey, | ||
`Invalid public key provided for algorithms ${expected.join(', ')}.` | ||
) | ||
} | ||
throw new TokenError( | ||
TokenError.codes.invalidKey, | ||
`Invalid public key provided for algorithms ${expected.join(', ')}.` | ||
) | ||
} | ||
@@ -44,9 +38,3 @@ | ||
// Only on Node 12 - Create a key object | ||
/* istanbul ignore next */ | ||
if (useNewCrypto) { | ||
key = isSecret ? createSecretKey(key) : createPublicKey(key) | ||
} | ||
return key | ||
return isSecret ? createSecretKey(key) : createPublicKey(key) | ||
} | ||
@@ -84,5 +72,6 @@ | ||
maxAge, | ||
clockTimestamp, | ||
clockTimestamp = Date.now(), | ||
clockTolerance, | ||
errorCacheTTL | ||
errorCacheTTL, | ||
cacheKeyBuilder | ||
}, | ||
@@ -99,4 +88,4 @@ value | ||
const ttl = typeof errorCacheTTL === 'function' ? errorCacheTTL(value) : errorCacheTTL | ||
cacheValue[2] = (clockTimestamp || Date.now()) + clockTolerance + ttl | ||
cache.set(hashToken(token), cacheValue) | ||
cacheValue[2] = clockTimestamp + clockTolerance + ttl | ||
cache.set(cacheKeyBuilder(token), cacheValue) | ||
return value | ||
@@ -121,6 +110,6 @@ } | ||
// The maximum TTL for the token cannot exceed the configured cacheTTL | ||
const maxTTL = (clockTimestamp || Date.now()) + clockTolerance + cacheTTL | ||
const maxTTL = clockTimestamp + clockTolerance + cacheTTL | ||
cacheValue[2] = cacheValue[2] === 0 ? maxTTL : Math.min(cacheValue[2], maxTTL) | ||
cache.set(hashToken(token), cacheValue) | ||
cache.set(cacheKeyBuilder(token), cacheValue) | ||
@@ -150,6 +139,4 @@ return value | ||
// According to the signature and key, check with algorithms are supported | ||
const algorithms = allowedAlgorithms | ||
// Verify the token is allowed | ||
if (!algorithms.includes(header.alg)) { | ||
if (!allowedAlgorithms.includes(header.alg)) { | ||
throw new TokenError(TokenError.codes.invalidAlgorithm, 'The token algorithm is invalid.') | ||
@@ -196,3 +183,3 @@ } | ||
{ input, header, payload, signature }, | ||
{ validators, allowedAlgorithms, checkTyp, clockTimestamp, clockTolerance, requiredClaims } | ||
{ validators, allowedAlgorithms, checkTyp, clockTimestamp, requiredClaims } | ||
) { | ||
@@ -212,11 +199,6 @@ // Verify the key | ||
// Verify typ | ||
if (checkTyp) { | ||
if (typeof header.typ !== 'string' || checkTyp !== header.typ.toLowerCase().replace(/^application\//, '')) { | ||
throw new TokenError(TokenError.codes.invalidType, 'Invalid typ.') | ||
} | ||
if (checkTyp && (typeof header.typ !== 'string' || checkTyp !== header.typ.toLowerCase().replace(/^application\//, ''))) { | ||
throw new TokenError(TokenError.codes.invalidType, 'Invalid typ.') | ||
} | ||
// Verify the payload | ||
const now = clockTimestamp || Date.now() | ||
if (requiredClaims) { | ||
@@ -230,4 +212,6 @@ for (const claim of requiredClaims) { | ||
for (const validator of validators) { | ||
const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } = validator | ||
// Verify the payload | ||
const now = clockTimestamp || Date.now() | ||
for (const { type, claim, allowed, array, modifier, greater, errorCode, errorVerb } of validators) { | ||
const value = payload[claim] | ||
@@ -271,3 +255,4 @@ const arrayValue = Array.isArray(value) | ||
requiredClaims, | ||
errorCacheTTL | ||
errorCacheTTL, | ||
cacheKeyBuilder | ||
}, | ||
@@ -279,18 +264,5 @@ token, | ||
const cacheContext = { | ||
cache, | ||
token, | ||
cacheTTL, | ||
errorCacheTTL, | ||
payload: undefined, | ||
ignoreExpiration, | ||
ignoreNotBefore, | ||
maxAge, | ||
clockTimestamp, | ||
clockTolerance | ||
} | ||
// Check the cache | ||
if (cache) { | ||
const [value, min, max] = cache.get(hashToken(token)) || [undefined, 0, 0] | ||
const [value, min, max] = cache.get(cacheKeyBuilder(token)) || [undefined, 0, 0] | ||
const now = clockTimestamp || Date.now() | ||
@@ -329,3 +301,15 @@ | ||
const { header, payload, signature } = decoded | ||
cacheContext.payload = payload | ||
const cacheContext = { | ||
cache, | ||
token, | ||
cacheTTL, | ||
errorCacheTTL, | ||
ignoreExpiration, | ||
ignoreNotBefore, | ||
maxAge, | ||
clockTimestamp, | ||
clockTolerance, | ||
payload, | ||
cacheKeyBuilder | ||
} | ||
const validationContext = { validators, allowedAlgorithms, checkTyp, clockTimestamp, clockTolerance, requiredClaims } | ||
@@ -408,4 +392,5 @@ | ||
allowedNonce, | ||
requiredClaims | ||
} = { cacheTTL: 600000, clockTolerance: 0, errorCacheTTL: -1, ...options } | ||
requiredClaims, | ||
cacheKeyBuilder | ||
} = { cacheTTL: 600_000, clockTolerance: 0, errorCacheTTL: -1, cacheKeyBuilder: hashToken, ...options } | ||
@@ -512,6 +497,3 @@ // Validate options | ||
let normalizedTyp = null | ||
if (checkTyp) { | ||
normalizedTyp = checkTyp.toLowerCase().replace(/^application\//, '') | ||
} | ||
const normalizedTyp = checkTyp ? checkTyp.toLowerCase().replace(/^application\//, '') : null | ||
@@ -534,3 +516,4 @@ const context = { | ||
cache: createCache(cacheSize), | ||
requiredClaims | ||
requiredClaims, | ||
cacheKeyBuilder | ||
} | ||
@@ -537,0 +520,0 @@ |
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
17
466
91153
1283
1