Comparing version 1.19.0 to 1.20.0
@@ -5,2 +5,11 @@ # Change Log | ||
# [1.20.0](https://github.com/panva/jose/compare/v1.19.0...v1.20.0) (2020-01-16) | ||
### Features | ||
* add JWTExpired error and JWTClaimInvalid claim and reason props ([a0c0c7a](https://github.com/panva/jose/commit/a0c0c7ad70f42d9b23b3e71de43599a8ac6fe1ff)), closes [#62](https://github.com/panva/jose/issues/62) | ||
# [1.19.0](https://github.com/panva/jose/compare/v1.18.2...v1.19.0) (2020-01-13) | ||
@@ -7,0 +16,0 @@ |
@@ -16,2 +16,3 @@ const CODES = { | ||
JWTClaimInvalid: 'ERR_JWT_CLAIM_INVALID', | ||
JWTExpired: 'ERR_JWT_EXPIRED', | ||
JWTMalformed: 'ERR_JWT_MALFORMED' | ||
@@ -77,3 +78,11 @@ } | ||
module.exports.JWTClaimInvalid = class JWTClaimInvalid extends JOSEError {} | ||
class JWTClaimInvalid extends JOSEError { | ||
constructor (message, claim = 'unspecified', reason = 'unspecified') { | ||
super(message) | ||
this.claim = claim | ||
this.reason = reason | ||
} | ||
} | ||
module.exports.JWTClaimInvalid = JWTClaimInvalid | ||
module.exports.JWTExpired = class JWTExpired extends JWTClaimInvalid {} | ||
module.exports.JWTMalformed = class JWTMalformed extends JOSEError {} |
const isNotString = val => typeof val !== 'string' || val.length === 0 | ||
module.exports.isNotString = isNotString | ||
module.exports.isString = function isString (Err, value, label, required = false) { | ||
module.exports.isString = function isString (Err, value, label, claim, required = false) { | ||
if (required && value === undefined) { | ||
throw new Err(`${label} is missing`) | ||
throw new Err(`${label} is missing`, claim, 'missing') | ||
} | ||
if (value !== undefined && isNotString(value)) { | ||
throw new Err(`${label} must be a string`) | ||
throw new Err(`${label} must be a string`, claim, 'invalid') | ||
} | ||
} |
@@ -7,3 +7,3 @@ const isObject = require('../help/is_object') | ||
const { KeyStore } = require('../jwks') | ||
const { JWTClaimInvalid } = require('../errors') | ||
const { JWTClaimInvalid, JWTExpired } = require('../errors') | ||
@@ -22,7 +22,7 @@ const { isString, isNotString } = require('./shared_validations') | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`) | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`, label, 'missing') | ||
} | ||
if (value !== undefined && (typeof value !== 'number' || !Number.isSafeInteger(value))) { | ||
throw new JWTClaimInvalid(`"${label}" claim must be a unix timestamp`) | ||
throw new JWTClaimInvalid(`"${label}" claim must be a unix timestamp`, label, 'invalid') | ||
} | ||
@@ -33,7 +33,7 @@ } | ||
if (required && value === undefined) { | ||
throw new JWTClaimInvalid(`"${label}" claim is missing`) | ||
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`) | ||
throw new JWTClaimInvalid(`"${label}" claim must be a string or array of strings`, label, 'invalid') | ||
} | ||
@@ -154,3 +154,3 @@ } | ||
const validateTypes = ({ header, payload }, profile, options) => { | ||
isPayloadString(header.alg, '"alg" header parameter', true) | ||
isPayloadString(header.alg, '"alg" header parameter', 'alg', true) | ||
@@ -161,18 +161,18 @@ isTimestamp(payload.iat, 'iat', profile === IDTOKEN || profile === LOGOUTTOKEN || !!options.maxTokenAge) | ||
isTimestamp(payload.nbf, 'nbf') | ||
isPayloadString(payload.jti, '"jti" claim', profile === LOGOUTTOKEN || !!options.jti) | ||
isPayloadString(payload.acr, '"acr" claim') | ||
isPayloadString(payload.nonce, '"nonce" claim', !!options.nonce) | ||
isPayloadString(payload.iss, '"iss" claim', profile === IDTOKEN || profile === ATJWT || profile === LOGOUTTOKEN || !!options.issuer) | ||
isPayloadString(payload.sub, '"sub" claim', profile === IDTOKEN || profile === ATJWT || !!options.subject) | ||
isPayloadString(payload.jti, '"jti" claim', 'jti', profile === LOGOUTTOKEN || !!options.jti) | ||
isPayloadString(payload.acr, '"acr" claim', 'acr') | ||
isPayloadString(payload.nonce, '"nonce" claim', 'nonce', !!options.nonce) | ||
isPayloadString(payload.iss, '"iss" claim', 'iss', profile === IDTOKEN || profile === ATJWT || profile === LOGOUTTOKEN || !!options.issuer) | ||
isPayloadString(payload.sub, '"sub" claim', 'sub', profile === IDTOKEN || profile === ATJWT || !!options.subject) | ||
isStringOrArrayOfStrings(payload.aud, 'aud', profile === IDTOKEN || profile === ATJWT || profile === LOGOUTTOKEN || !!options.audience) | ||
isPayloadString(payload.azp, '"azp" claim', profile === IDTOKEN && Array.isArray(payload.aud) && payload.aud.length > 1) | ||
isPayloadString(payload.azp, '"azp" claim', 'azp', profile === IDTOKEN && Array.isArray(payload.aud) && payload.aud.length > 1) | ||
isStringOrArrayOfStrings(payload.amr, 'amr') | ||
if (profile === ATJWT) { | ||
isPayloadString(payload.client_id, '"client_id" claim', true) | ||
isPayloadString(header.typ, '"typ" header parameter', true) | ||
isPayloadString(payload.client_id, '"client_id" claim', 'client_id', true) | ||
isPayloadString(header.typ, '"typ" header parameter', 'typ', true) | ||
} | ||
if (profile === LOGOUTTOKEN) { | ||
isPayloadString(payload.sid, '"sid" claim') | ||
isPayloadString(payload.sid, '"sid" claim', 'sid') | ||
@@ -184,19 +184,19 @@ if (!('sid' in payload) && !('sub' in payload)) { | ||
if ('nonce' in payload) { | ||
throw new JWTClaimInvalid('"nonce" claim is prohibited') | ||
throw new JWTClaimInvalid('"nonce" claim is prohibited', 'nonce', 'prohibited') | ||
} | ||
if (!('events' in payload)) { | ||
throw new JWTClaimInvalid('"events" claim is missing') | ||
throw new JWTClaimInvalid('"events" claim is missing', 'events', 'missing') | ||
} | ||
if (!isObject(payload.events)) { | ||
throw new JWTClaimInvalid('"events" claim must be an object') | ||
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') | ||
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') | ||
throw new JWTClaimInvalid('"http://schemas.openid.net/event/backchannel-logout" member in the "events" claim must be an object', 'events', 'invalid') | ||
} | ||
@@ -240,19 +240,19 @@ } | ||
if (issuer && decoded.payload.iss !== issuer) { | ||
throw new JWTClaimInvalid('unexpected "iss" claim value') | ||
throw new JWTClaimInvalid('unexpected "iss" claim value', 'iss', 'check_failed') | ||
} | ||
if (nonce && decoded.payload.nonce !== nonce) { | ||
throw new JWTClaimInvalid('unexpected "nonce" claim value') | ||
throw new JWTClaimInvalid('unexpected "nonce" claim value', 'nonce', 'check_failed') | ||
} | ||
if (subject && decoded.payload.sub !== subject) { | ||
throw new JWTClaimInvalid('unexpected "sub" claim value') | ||
throw new JWTClaimInvalid('unexpected "sub" claim value', 'sub', 'check_failed') | ||
} | ||
if (jti && decoded.payload.jti !== jti) { | ||
throw new JWTClaimInvalid('unexpected "jti" claim value') | ||
throw new JWTClaimInvalid('unexpected "jti" claim value', 'jti', 'check_failed') | ||
} | ||
if (audience && !checkAudiencePresence(decoded.payload.aud, typeof audience === 'string' ? [audience] : audience, profile)) { | ||
throw new JWTClaimInvalid('unexpected "aud" claim value') | ||
throw new JWTClaimInvalid('unexpected "aud" claim value', 'aud', 'check_failed') | ||
} | ||
@@ -265,3 +265,3 @@ | ||
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)') | ||
throw new JWTClaimInvalid('"auth_time" claim timestamp check failed (too much time has elapsed since the last End-User authentication)', 'auth_time', 'check_failed') | ||
} | ||
@@ -271,11 +271,11 @@ } | ||
if (!ignoreIat && !('exp' in decoded.payload) && 'iat' in decoded.payload && decoded.payload.iat > unix + tolerance) { | ||
throw new JWTClaimInvalid('"iat" claim timestamp check failed (it should be in the past)') | ||
throw new JWTClaimInvalid('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed') | ||
} | ||
if (!ignoreNbf && 'nbf' in decoded.payload && decoded.payload.nbf > unix + tolerance) { | ||
throw new JWTClaimInvalid('"nbf" claim timestamp check failed') | ||
throw new JWTClaimInvalid('"nbf" claim timestamp check failed', 'nbf', 'check_failed') | ||
} | ||
if (!ignoreExp && 'exp' in decoded.payload && decoded.payload.exp <= unix - tolerance) { | ||
throw new JWTClaimInvalid('"exp" claim timestamp check failed') | ||
throw new JWTExpired('"exp" claim timestamp check failed', 'exp', 'check_failed') | ||
} | ||
@@ -288,7 +288,7 @@ | ||
if (age - tolerance > max) { | ||
throw new JWTClaimInvalid('"iat" claim timestamp check failed (too far in the past)') | ||
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', 'iat', 'check_failed') | ||
} | ||
if (age < 0 - tolerance) { | ||
throw new JWTClaimInvalid('"iat" claim timestamp check failed (it should be in the past)') | ||
throw new JWTClaimInvalid('"iat" claim timestamp check failed (it should be in the past)', 'iat', 'check_failed') | ||
} | ||
@@ -298,7 +298,7 @@ } | ||
if (profile === IDTOKEN && Array.isArray(decoded.payload.aud) && decoded.payload.aud.length > 1 && decoded.payload.azp !== audience) { | ||
throw new JWTClaimInvalid('unexpected "azp" claim value') | ||
throw new JWTClaimInvalid('unexpected "azp" claim value', 'azp', 'check_failed') | ||
} | ||
if (profile === ATJWT && decoded.header.typ !== ATJWT) { | ||
throw new JWTClaimInvalid('invalid JWT typ header value for the used validation profile') | ||
throw new JWTClaimInvalid('invalid JWT typ header value for the used validation profile', 'typ', 'check_failed') | ||
} | ||
@@ -305,0 +305,0 @@ |
{ | ||
"name": "jose", | ||
"version": "1.19.0", | ||
"version": "1.20.0", | ||
"description": "JSON Web Almost Everything - JWA, JWS, JWE, JWK, JWT, JWKS for Node.js with minimal dependencies", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -410,23 +410,33 @@ /// <reference types="node" /> | ||
export namespace errors { | ||
class JOSEError extends Error {} | ||
class JOSEMultiError extends JOSEError {} | ||
class JOSEError<T = string> extends Error { | ||
code: T; | ||
} | ||
class JOSEAlgNotWhitelisted extends JOSEError {} | ||
class JOSECritNotUnderstood extends JOSEError {} | ||
class JOSENotSupported extends JOSEError {} | ||
class JOSEInvalidEncoding extends JOSEError<'ERR_JOSE_INVALID_ENCODING'> {} | ||
class JOSEMultiError extends JOSEError<'ERR_JOSE_MULTIPLE_ERRORS'> {} | ||
class JWEDecryptionFailed extends JOSEError {} | ||
class JWEInvalid extends JOSEError {} | ||
class JOSEAlgNotWhitelisted extends JOSEError<'ERR_JOSE_ALG_NOT_WHITELISTED'> {} | ||
class JOSECritNotUnderstood extends JOSEError<'ERR_JOSE_CRIT_NOT_UNDERSTOOD'> {} | ||
class JOSENotSupported extends JOSEError<'ERR_JOSE_NOT_SUPPORTED'> {} | ||
class JWKImportFailed extends JOSEError {} | ||
class JWKInvalid extends JOSEError {} | ||
class JWKKeySupport extends JOSEError {} | ||
class JWEDecryptionFailed extends JOSEError<'ERR_JWE_DECRYPTION_FAILED'> {} | ||
class JWEInvalid extends JOSEError<'ERR_JWE_INVALID'> {} | ||
class JWKSNoMatchingKey extends JOSEError {} | ||
class JWKImportFailed extends JOSEError<'ERR_JWK_IMPORT_FAILED'> {} | ||
class JWKInvalid extends JOSEError<'ERR_JWK_INVALID'> {} | ||
class JWKKeySupport extends JOSEError<'ERR_JWK_KEY_SUPPORT'> {} | ||
class JWSInvalid extends JOSEError {} | ||
class JWSVerificationFailed extends JOSEError {} | ||
class JWKSNoMatchingKey extends JOSEError<'ERR_JWKS_NO_MATCHING_KEY'> {} | ||
class JWTClaimInvalid extends JOSEError {} | ||
class JWTMalformed extends JOSEError {} | ||
class JWSInvalid extends JOSEError<'ERR_JWS_INVALID'> {} | ||
class JWSVerificationFailed extends JOSEError<'ERR_JWS_VERIFICATION_FAILED'> {} | ||
class JWTClaimInvalid<T = 'ERR_JWT_CLAIM_INVALID'> extends JOSEError<T> { | ||
constructor(message?: string, claim?: string, reason?: string); | ||
claim: string; | ||
reason: 'prohibited' | 'missing' | 'invalid' | 'check_failed' | 'unspecified'; | ||
} | ||
class JWTExpired extends JWTClaimInvalid<'ERR_JWT_EXPIRED'> {} | ||
class JWTMalformed extends JOSEError<'ERR_JWT_MALFORMED'> {} | ||
} |
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
225693
4819