@guarani/jose
Version:
Implementation of the RFCs of the JOSE Working Group.
144 lines (143 loc) • 7.02 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonWebTokenClaims = void 0;
const objects_1 = require("@guarani/objects");
const util_1 = require("util");
const expired_token_exception_1 = require("../exceptions/expired-token.exception");
const invalid_json_web_token_claim_exception_1 = require("../exceptions/invalid-json-web-token-claim.exception");
const invalid_json_web_token_exception_1 = require("../exceptions/invalid-json-web-token.exception");
const jose_exception_1 = require("../exceptions/jose.exception");
const token_not_valid_yet_exception_1 = require("../exceptions/token-not-valid-yet.exception");
/**
* Implementation of {@link https://www.rfc-editor.org/rfc/rfc7519.html#section-4 RFC 7519 Section 4}.
*/
class JsonWebTokenClaims {
/**
* Instantiates a new JSON Web Token Claims for usage with JSON Web Tokens.
*
* @param claims Defines the claims of the JSON Web Token.
* @param options Validation options for the claims.
*/
constructor(claims, options = {}) {
if (claims instanceof JsonWebTokenClaims) {
return claims;
}
this.validateDefaultClaims(claims);
this.validateCustomClaims?.(claims);
this.validateClaimsOptions(claims, options);
Object.assign(this, (0, objects_1.removeNullishValues)(claims));
}
/**
* Parses the provided data into a JSON Web Token Claims object.
*
* @param data Encoded JSON Web Token Claims.
* @param options Validation options for the claims.
* @returns Parsed JSON Web Token Claims.
*/
static parse(data, options) {
try {
if (typeof data === 'string') {
data = Buffer.from(data, 'base64url');
}
const claims = JSON.parse(data.toString('utf8'));
return new JsonWebTokenClaims(claims, options);
}
catch (exc) {
if (exc instanceof jose_exception_1.JoseException) {
throw exc;
}
throw new invalid_json_web_token_exception_1.InvalidJsonWebTokenException();
}
}
/**
* Validates the Default JSON Web Token Claims based on the rules of
* {@link https://www.rfc-editor.org/rfc/rfc7519.html#section-4 RFC 7519 Section 4}.
*
* @param claims JSON Web Token Claims.
*/
validateDefaultClaims(claims) {
const now = Math.floor(Date.now() / 1000);
if (claims.iss !== undefined && (typeof claims.iss !== 'string' || claims.iss.length === 0)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "iss".');
}
if (claims.sub !== undefined && (typeof claims.sub !== 'string' || claims.sub.length === 0)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "sub".');
}
if (claims.aud !== undefined) {
if (typeof claims.aud !== 'string' && !Array.isArray(claims.aud)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "aud".');
}
if (Array.isArray(claims.aud) && claims.aud.some((aud) => typeof aud !== 'string' || aud.length === 0)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "aud".');
}
if (typeof claims.aud === 'string' && claims.aud.length === 0) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "aud".');
}
}
if (claims.exp !== undefined) {
if (typeof claims.exp !== 'number') {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "exp".');
}
if (now > claims.exp) {
throw new expired_token_exception_1.ExpiredTokenException();
}
}
if (claims.nbf !== undefined) {
if (typeof claims.nbf !== 'number') {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "nbf".');
}
if (now < claims.nbf) {
throw new token_not_valid_yet_exception_1.TokenNotValidYetException();
}
}
if (claims.iat !== undefined && typeof claims.iat !== 'number') {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "iat".');
}
if (claims.jti !== undefined && (typeof claims.jti !== 'string' || claims.jti.length === 0)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Invalid claim "jti".');
}
}
/**
* Validates the provided JSON Web Token Claims based on the provided Options.
*
* @param claims JSON Web Token Claims.
* @param options Dictionary used to validate the provided JSON Web Token Claims.
*/
validateClaimsOptions(claims, options) {
Object.entries(options).forEach(([claim, option]) => {
const claimValue = claims[claim];
if (option.essential === true && claimValue === undefined) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException(`Missing required claim "${claim}".`);
}
if (option.value !== undefined && !(0, util_1.isDeepStrictEqual)(claimValue, option.value)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException(`Mismatching expected value for claim "${claim}".`);
}
if (option.values !== undefined) {
if (!Array.isArray(option.values)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException('Expected an array for the option "values".');
}
if (option.values.length === 0) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException(`Mismatching expected value for claim "${claim}".`);
}
option.values.forEach((value) => {
if (!(0, util_1.isDeepStrictEqual)(value, claimValue)) {
throw new invalid_json_web_token_claim_exception_1.InvalidJsonWebTokenClaimException(`Mismatching expected value for claim "${claim}". Received "${claimValue}".`);
}
});
}
});
}
/**
* Returns the JSON Encoded String of the JSON Web Token Claims.
*/
toString() {
return JSON.stringify(this);
}
/**
* Returns the Buffer representation of the JSON Encoded String of the JSON Web Token Claims.
*/
toBuffer() {
return Buffer.from(this.toString(), 'utf8');
}
}
exports.JsonWebTokenClaims = JsonWebTokenClaims;