mirror of
https://github.com/danbulant/jose
synced 2026-05-21 21:39:04 +00:00
perf: base64url decode, JWT.verify, JWK.Key instance re-use
I'm done trying to educate other JOSE producers about interoperability so i'm going to be accepting their non-conform base64url so that users of this module don't suffer performance loss.
This commit is contained in:
parent
955171701f
commit
470b4c7315
11 changed files with 71 additions and 95 deletions
|
|
@ -25,7 +25,22 @@ require('./pbes2')(JWA, JWK)
|
|||
require('./ecdh/dir')(JWA, JWK)
|
||||
require('./ecdh/kw')(JWA, JWK)
|
||||
|
||||
const map = new WeakMap()
|
||||
|
||||
const i = (ctx) => {
|
||||
if (!map.has(ctx)) {
|
||||
map.set(ctx, {})
|
||||
}
|
||||
return map.get(ctx)
|
||||
}
|
||||
|
||||
const check = (key, op, alg) => {
|
||||
const cache = i(key)
|
||||
|
||||
if (cache[`${op}${alg}`]) {
|
||||
return true
|
||||
}
|
||||
|
||||
let label
|
||||
let keyOp
|
||||
if (op === 'keyManagementEncrypt') {
|
||||
|
|
@ -43,6 +58,8 @@ const check = (key, op, alg) => {
|
|||
} else {
|
||||
throw new JOSENotSupported(`unsupported ${label || op} alg: ${alg}`)
|
||||
}
|
||||
|
||||
cache[`${op}${alg}`] = true
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -55,8 +55,6 @@ const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], c
|
|||
|
||||
if (!serialization) {
|
||||
serialization = resolveSerialization(jwe)
|
||||
} else if (serialization !== resolveSerialization(jwe)) {
|
||||
throw new errors.JWEInvalid()
|
||||
}
|
||||
|
||||
let alg, ciphertext, enc, encryptedKey, iv, opts, prot, tag, unprotected, cek, aad, header
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const Sign = require('./sign')
|
||||
const verify = require('./verify')
|
||||
const { verify } = require('./verify')
|
||||
|
||||
const single = (serialization, payload, key, protectedHeader, unprotectedHeader) => {
|
||||
const jws = new Sign(payload)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const { check, verify } = require('../jwa')
|
|||
const { detect: resolveSerialization } = require('./serializers')
|
||||
|
||||
validateCrit = validateCrit.bind(undefined, errors.JWSInvalid)
|
||||
const SINGLE_RECIPIENT = new Set(['compact', 'flattened'])
|
||||
const SINGLE_RECIPIENT = new Set(['compact', 'flattened', 'preparsed'])
|
||||
|
||||
/*
|
||||
* @public
|
||||
|
|
@ -29,8 +29,6 @@ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], comp
|
|||
|
||||
if (!serialization) {
|
||||
serialization = resolveSerialization(jws)
|
||||
} else if (serialization !== resolveSerialization(jws)) {
|
||||
throw new errors.JWSInvalid()
|
||||
}
|
||||
|
||||
let prot // protected header
|
||||
|
|
@ -47,26 +45,35 @@ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], comp
|
|||
jws = { ...root, ...signatures[0] }
|
||||
}
|
||||
|
||||
let decoded
|
||||
|
||||
if (SINGLE_RECIPIENT.has(serialization)) {
|
||||
if (serialization === 'compact') { // compact serialization format
|
||||
([prot, payload, signature] = jws.split('.'))
|
||||
} else { // flattened serialization format
|
||||
({ protected: prot, payload, signature, header } = jws)
|
||||
let parsedProt = {}
|
||||
|
||||
switch (serialization) {
|
||||
case 'compact': // compact serialization format
|
||||
([prot, payload, signature] = jws.split('.'))
|
||||
break
|
||||
case 'flattened': // flattened serialization format
|
||||
({ protected: prot, payload, signature, header } = jws)
|
||||
break
|
||||
case 'preparsed': { // from the JWT module
|
||||
({ decoded } = jws);
|
||||
([prot, payload, signature] = jws.token.split('.'))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!header) {
|
||||
skipDisjointCheck = true
|
||||
}
|
||||
|
||||
let parsedProt = {}
|
||||
if (prot) {
|
||||
if (decoded) {
|
||||
parsedProt = decoded.header
|
||||
} else if (prot) {
|
||||
try {
|
||||
parsedProt = base64url.JSON.decode(prot)
|
||||
} catch (err) {
|
||||
if (err instanceof errors.JOSEError) {
|
||||
throw err
|
||||
}
|
||||
|
||||
throw new errors.JWSInvalid('could not parse JWS protected header')
|
||||
}
|
||||
} else {
|
||||
|
|
@ -125,13 +132,14 @@ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], comp
|
|||
Buffer.from('.'),
|
||||
Buffer.isBuffer(payload) ? payload : Buffer.from(payload)
|
||||
])
|
||||
|
||||
if (!verify(alg, key, toBeVerified, base64url.decodeToBuffer(signature))) {
|
||||
throw new errors.JWSVerificationFailed()
|
||||
}
|
||||
|
||||
if (!combinedHeader.crit || !combinedHeader.crit.includes('b64') || combinedHeader.b64) {
|
||||
if (parse) {
|
||||
payload = base64url.JSON.decode.try(payload, encoding)
|
||||
payload = decoded ? decoded.payload : base64url.JSON.decode.try(payload, encoding)
|
||||
} else {
|
||||
payload = base64url.decodeToBuffer(payload)
|
||||
}
|
||||
|
|
@ -168,4 +176,7 @@ const jwsVerify = (skipDisjointCheck, serialization, jws, key, { crit = [], comp
|
|||
throw multi
|
||||
}
|
||||
|
||||
module.exports = jwsVerify.bind(undefined, false, undefined)
|
||||
module.exports = {
|
||||
bare: jwsVerify,
|
||||
verify: jwsVerify.bind(undefined, false, undefined)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const isObject = require('../help/is_object')
|
|||
const epoch = require('../help/epoch')
|
||||
const secs = require('../help/secs')
|
||||
const getKey = require('../help/get_key')
|
||||
const JWS = require('../jws')
|
||||
const { bare: verify } = require('../jws/verify')
|
||||
const { KeyStore } = require('../jwks')
|
||||
const { JWTClaimInvalid, JWTExpired } = require('../errors')
|
||||
|
||||
|
|
@ -225,9 +225,17 @@ module.exports = (token, key, options = {}) => {
|
|||
jti, maxAuthAge, maxTokenAge, nonce, now, profile, subject
|
||||
} = options = validateOptions(options)
|
||||
|
||||
const unix = epoch(now)
|
||||
|
||||
const decoded = decode(token, { complete: true })
|
||||
key = getKey(key, true)
|
||||
|
||||
if (complete) {
|
||||
({ key } = verify(true, 'preparsed', { decoded, token }, key, { crit, algorithms, complete: true }))
|
||||
decoded.key = key
|
||||
} else {
|
||||
verify(true, 'preparsed', { decoded, token }, key, { crit, algorithms })
|
||||
}
|
||||
|
||||
const unix = epoch(now)
|
||||
validateTypes(decoded, profile, options)
|
||||
|
||||
if (issuer && decoded.payload.iss !== issuer) {
|
||||
|
|
@ -292,13 +300,5 @@ module.exports = (token, key, options = {}) => {
|
|||
throw new JWTClaimInvalid('invalid JWT typ header value for the used validation profile', 'typ', 'check_failed')
|
||||
}
|
||||
|
||||
key = getKey(key, true)
|
||||
|
||||
if (complete && key instanceof KeyStore) {
|
||||
({ key } = JWS.verify(token, key, { crit, algorithms, complete: true }))
|
||||
} else {
|
||||
JWS.verify(token, key, { crit, algorithms })
|
||||
}
|
||||
|
||||
return complete ? { ...decoded, key } : decoded.payload
|
||||
return complete ? decoded : decoded.payload
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,9 +41,3 @@ test('.JSON.decode.try (valid json)', t => {
|
|||
test('.JSON.decode.try (invalid json)', t => {
|
||||
t.is(base64url.JSON.decode.try('Zm9v'), 'foo')
|
||||
})
|
||||
|
||||
test('decode input with invalid encoding throws', t => {
|
||||
t.throws(() => {
|
||||
base64url.decode(testStr)
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const test = require('ava')
|
|||
|
||||
const { errors } = require('../..')
|
||||
const JWA = require('../../lib/jwa')
|
||||
const JWK = require('../../lib/jwk')
|
||||
|
||||
;['sign', 'verify', 'keyManagementEncrypt', 'keyManagementDecrypt', 'encrypt', 'decrypt'].forEach((op) => {
|
||||
let label
|
||||
|
|
@ -10,7 +11,7 @@ const JWA = require('../../lib/jwa')
|
|||
}
|
||||
test(`JWA.${op} will not accept an "unimplemented" algorithm`, t => {
|
||||
t.throws(() => {
|
||||
JWA[op]('foo')
|
||||
JWA[op]('foo', JWK.generateSync('oct'))
|
||||
}, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: `unsupported ${label || op} alg: foo` })
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -145,26 +145,6 @@ test('fails to import JWK RSA with oth', async t => {
|
|||
}, { instanceOf: errors.JOSENotSupported, code: 'ERR_JOSE_NOT_SUPPORTED', message: 'Private RSA keys with more than two primes are not supported' })
|
||||
})
|
||||
|
||||
test('invalid encoded jwk import', async t => {
|
||||
const jwk = (await generate('oct')).toJWK(true)
|
||||
|
||||
jwk.k = base64url.decodeToBuffer(jwk.k).toString('base64')
|
||||
|
||||
t.throws(() => {
|
||||
asKey(jwk)
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
||||
test('invalid encoded oct jwk import', async t => {
|
||||
const jwk = (await generate('EC')).toJWK(true)
|
||||
|
||||
jwk.d = base64url.decodeToBuffer(jwk.d).toString('base64')
|
||||
|
||||
t.throws(() => {
|
||||
asKey(jwk)
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
||||
const cert = `-----BEGIN CERTIFICATE-----
|
||||
MIIC4DCCAcgCCQDO8JBSH914NDANBgkqhkiG9w0BAQsFADAyMQswCQYDVQQGEwJD
|
||||
WjEPMA0GA1UEBwwGUHJhZ3VlMRIwEAYDVQQDDAlwa210bHN0d28wHhcNMTkwNjE4
|
||||
|
|
|
|||
|
|
@ -273,15 +273,6 @@ test('JWS verify algorithms whitelist (multi-recipient)', t => {
|
|||
})
|
||||
})
|
||||
|
||||
test('invalid tokens', t => {
|
||||
t.throws(() => {
|
||||
JWS.verify(
|
||||
'eyJ0eXAiOiJKV1QiLCJraWQiOiIyZTFkYjRmMC1mYmY5LTQxZjYtOGMxYi1hMzczYjgwZmNhYTEiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHktc3RhZ2luZy5kZWxpdmVyb28uY29tLyIsImNsaWVudCI6ImIyM2I0ZjM1YzIyMTI5NDQxZjMwZDMyYmI5ZmM4ZWYyIiwic2lnbmVyIjoiYXJuOmF3czplbGFzdGljbG9hZGJhbGFuY2luZzpldS13ZXN0LTE6NTE3OTAyNjYzOTE1OmxvYWRiYWxhbmNlci9hcHAvcGF5bWVudHMtZGFzaGJvYXJkLXdlYi80YzA4ZGI2NDMyMDIyOWEyIiwiZXhwIjoxNTYyNjkxNTg1fQ==.eyJlbWFpbCI6ImpvYW8udmllaXJhQGRlbGl2ZXJvby5jby51ayIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmYW1pbHlfbmFtZSI6Ikd1ZXJyYSBWaWVpcmEiLCJnaXZlbl9uYW1lIjoiSm9hbyIsIm5hbWUiOiJKb2FvIEd1ZXJyYSBWaWVpcmEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1sTUpXTXV3R1dpYy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCVS9lNGtkTDg5UjlqZy9zOTYtYy9waG90by5qcGciLCJzdWIiOiIxMWE1YmFmMGRjNzcwNWRmMzk1ZTMzYWFkZjU2MDk4OCIsImV4cCI6MTU2MjY5MTU4NSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS1zdGFnaW5nLmRlbGl2ZXJvby5jb20vIn0=.DSHLJXLOfLJ-ZYcX0Vlii6Ak_jcDSkKOvNRj_rvtAyY9uYXtwo798ZrR35fgut-LuCdx0aKz2SgK0KJqw5q6dA==',
|
||||
generateSync('EC')
|
||||
)
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
||||
test('"enc" key is not usable for signing', t => {
|
||||
const k = generateSync('oct', 256, { use: 'enc' })
|
||||
t.throws(() => {
|
||||
|
|
|
|||
|
|
@ -47,9 +47,3 @@ test('returns the payload', t => {
|
|||
foo: 'bar'
|
||||
})
|
||||
})
|
||||
|
||||
test('invalid tokens', t => {
|
||||
t.throws(() => {
|
||||
JWT.decode('eyJ0eXAiOiJKV1QiLCJraWQiOiIyZTFkYjRmMC1mYmY5LTQxZjYtOGMxYi1hMzczYjgwZmNhYTEiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHktc3RhZ2luZy5kZWxpdmVyb28uY29tLyIsImNsaWVudCI6ImIyM2I0ZjM1YzIyMTI5NDQxZjMwZDMyYmI5ZmM4ZWYyIiwic2lnbmVyIjoiYXJuOmF3czplbGFzdGljbG9hZGJhbGFuY2luZzpldS13ZXN0LTE6NTE3OTAyNjYzOTE1OmxvYWRiYWxhbmNlci9hcHAvcGF5bWVudHMtZGFzaGJvYXJkLXdlYi80YzA4ZGI2NDMyMDIyOWEyIiwiZXhwIjoxNTYyNjkxNTg1fQ==.eyJlbWFpbCI6ImpvYW8udmllaXJhQGRlbGl2ZXJvby5jby51ayIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmYW1pbHlfbmFtZSI6Ikd1ZXJyYSBWaWVpcmEiLCJnaXZlbl9uYW1lIjoiSm9hbyIsIm5hbWUiOiJKb2FvIEd1ZXJyYSBWaWVpcmEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1sTUpXTXV3R1dpYy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCVS9lNGtkTDg5UjlqZy9zOTYtYy9waG90by5qcGciLCJzdWIiOiIxMWE1YmFmMGRjNzcwNWRmMzk1ZTMzYWFkZjU2MDk4OCIsImV4cCI6MTU2MjY5MTU4NSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS1zdGFnaW5nLmRlbGl2ZXJvby5jb20vIn0=.DSHLJXLOfLJ-ZYcX0Vlii6Ak_jcDSkKOvNRj_rvtAyY9uYXtwo798ZrR35fgut-LuCdx0aKz2SgK0KJqw5q6dA==')
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
const test = require('ava')
|
||||
|
||||
const { JWT, JWK, JWKS, errors } = require('../..')
|
||||
const base64url = require('../../lib/help/base64url')
|
||||
const { JWS, JWT, JWK, JWKS, errors } = require('../..')
|
||||
|
||||
const key = JWK.generateSync('oct')
|
||||
const token = JWT.sign({}, key, { iat: false })
|
||||
|
|
@ -107,7 +106,7 @@ test('options.ignoreIat & options.maxTokenAge may not be used together', t => {
|
|||
test(`"${claim} must be a timestamp when provided"`, t => {
|
||||
;['', 'foo', true, null, [], {}].forEach((val) => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: val })}.`
|
||||
const invalid = JWS.sign({ [claim]: val }, key)
|
||||
JWT.verify(invalid, key)
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `"${claim}" claim must be a unix timestamp` })
|
||||
|
||||
|
|
@ -121,7 +120,7 @@ test('options.ignoreIat & options.maxTokenAge may not be used together', t => {
|
|||
test(`"${claim} must be a string when provided"`, t => {
|
||||
;['', 0, 1, true, null, [], {}].forEach((val) => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: val })}.`
|
||||
const invalid = JWS.sign({ [claim]: val }, key)
|
||||
JWT.verify(invalid, key)
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `"${claim}" claim must be a string` })
|
||||
|
||||
|
|
@ -136,14 +135,14 @@ test('options.ignoreIat & options.maxTokenAge may not be used together', t => {
|
|||
;['', 0, 1, true, null, [], {}].forEach((val) => {
|
||||
let err
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: val })}.`
|
||||
const invalid = JWS.sign({ [claim]: val }, key)
|
||||
JWT.verify(invalid, key)
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `"${claim}" claim must be a string or array of strings` })
|
||||
t.is(err.claim, claim)
|
||||
t.is(err.reason, 'invalid')
|
||||
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: [val] })}.`
|
||||
const invalid = JWS.sign({ [claim]: [val] }, key)
|
||||
JWT.verify(invalid, key)
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `"${claim}" claim must be a string or array of strings` })
|
||||
t.is(err.claim, claim)
|
||||
|
|
@ -161,14 +160,14 @@ Object.entries({
|
|||
test(`option.${option} validation fails`, t => {
|
||||
let err
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: 'foo' })}.`
|
||||
const invalid = JWS.sign({ [claim]: 'foo' }, key)
|
||||
JWT.verify(invalid, key, { [option]: 'bar' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `unexpected "${claim}" claim value` })
|
||||
t.is(err.claim, claim)
|
||||
t.is(err.reason, 'check_failed')
|
||||
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ [claim]: undefined })}.`
|
||||
const invalid = JWS.sign({ [claim]: undefined }, key)
|
||||
JWT.verify(invalid, key, { [option]: 'bar' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: `"${claim}" claim is missing` })
|
||||
t.is(err.claim, claim)
|
||||
|
|
@ -185,14 +184,14 @@ Object.entries({
|
|||
test('option.audience validation fails', t => {
|
||||
let err
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ aud: 'foo' })}.`
|
||||
const invalid = JWS.sign({ aud: 'foo' }, key)
|
||||
JWT.verify(invalid, key, { audience: 'bar' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: 'unexpected "aud" claim value' })
|
||||
t.is(err.claim, 'aud')
|
||||
t.is(err.reason, 'check_failed')
|
||||
|
||||
err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ aud: ['foo'] })}.`
|
||||
const invalid = JWS.sign({ aud: ['foo'] }, key)
|
||||
JWT.verify(invalid, key, { audience: 'bar' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: 'unexpected "aud" claim value' })
|
||||
t.is(err.claim, 'aud')
|
||||
|
|
@ -218,7 +217,7 @@ test('option.audience validation success', t => {
|
|||
|
||||
test('option.maxAuthAge requires iat to be in the payload', t => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({})}.`
|
||||
const invalid = JWS.sign({}, key)
|
||||
JWT.verify(invalid, key, { maxAuthAge: '30s' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: '"auth_time" claim is missing' })
|
||||
t.is(err.claim, 'auth_time')
|
||||
|
|
@ -230,7 +229,7 @@ const now = new Date(epoch * 1000)
|
|||
|
||||
test('option.maxAuthAge checks auth_time', t => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ auth_time: epoch - 31 })}.`
|
||||
const invalid = JWS.sign({ auth_time: epoch - 31 }, key)
|
||||
JWT.verify(invalid, key, { maxAuthAge: '30s', now })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: '"auth_time" claim timestamp check failed (too much time has elapsed since the last End-User authentication)' })
|
||||
t.is(err.claim, 'auth_time')
|
||||
|
|
@ -245,7 +244,7 @@ test('option.maxAuthAge checks auth_time (with tolerance)', t => {
|
|||
|
||||
test('option.maxTokenAge requires iat to be in the payload', t => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({})}.`
|
||||
const invalid = JWS.sign({}, key)
|
||||
JWT.verify(invalid, key, { maxTokenAge: '30s' })
|
||||
}, { instanceOf: errors.JWTClaimInvalid, message: '"iat" claim is missing' })
|
||||
t.is(err.claim, 'iat')
|
||||
|
|
@ -254,7 +253,7 @@ test('option.maxTokenAge requires iat to be in the payload', t => {
|
|||
|
||||
test('option.maxTokenAge checks iat elapsed time', t => {
|
||||
const err = t.throws(() => {
|
||||
const invalid = `eyJhbGciOiJub25lIn0.${base64url.JSON.encode({ iat: epoch - 31 })}.`
|
||||
const invalid = JWS.sign({ iat: epoch - 31 }, key)
|
||||
JWT.verify(invalid, key, { maxTokenAge: '30s', now })
|
||||
}, { instanceOf: errors.JWTExpired, code: 'ERR_JWT_EXPIRED', message: '"iat" claim timestamp check failed (too far in the past)' })
|
||||
t.true(err instanceof errors.JWTClaimInvalid)
|
||||
|
|
@ -828,12 +827,3 @@ test('must be a supported value', t => {
|
|||
t.is(err.reason, 'check_failed')
|
||||
})
|
||||
}
|
||||
|
||||
test('invalid tokens', t => {
|
||||
t.throws(() => {
|
||||
JWT.verify(
|
||||
'eyJ0eXAiOiJKV1QiLCJraWQiOiIyZTFkYjRmMC1mYmY5LTQxZjYtOGMxYi1hMzczYjgwZmNhYTEiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vaWRlbnRpdHktc3RhZ2luZy5kZWxpdmVyb28uY29tLyIsImNsaWVudCI6ImIyM2I0ZjM1YzIyMTI5NDQxZjMwZDMyYmI5ZmM4ZWYyIiwic2lnbmVyIjoiYXJuOmF3czplbGFzdGljbG9hZGJhbGFuY2luZzpldS13ZXN0LTE6NTE3OTAyNjYzOTE1OmxvYWRiYWxhbmNlci9hcHAvcGF5bWVudHMtZGFzaGJvYXJkLXdlYi80YzA4ZGI2NDMyMDIyOWEyIiwiZXhwIjoxNTYyNjkxNTg1fQ==.eyJlbWFpbCI6ImpvYW8udmllaXJhQGRlbGl2ZXJvby5jby51ayIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmYW1pbHlfbmFtZSI6Ikd1ZXJyYSBWaWVpcmEiLCJnaXZlbl9uYW1lIjoiSm9hbyIsIm5hbWUiOiJKb2FvIEd1ZXJyYSBWaWVpcmEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1sTUpXTXV3R1dpYy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCVS9lNGtkTDg5UjlqZy9zOTYtYy9waG90by5qcGciLCJzdWIiOiIxMWE1YmFmMGRjNzcwNWRmMzk1ZTMzYWFkZjU2MDk4OCIsImV4cCI6MTU2MjY5MTU4NSwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS1zdGFnaW5nLmRlbGl2ZXJvby5jb20vIn0=.DSHLJXLOfLJ-ZYcX0Vlii6Ak_jcDSkKOvNRj_rvtAyY9uYXtwo798ZrR35fgut-LuCdx0aKz2SgK0KJqw5q6dA==',
|
||||
key
|
||||
)
|
||||
}, { instanceOf: errors.JOSEInvalidEncoding, code: 'ERR_JOSE_INVALID_ENCODING', message: 'input is not a valid base64url encoded string' })
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue