@villedemontreal/jwt-validator
Version:
Module to validate JWT (JSON Web Tokens)
130 lines • 5.38 kB
JavaScript
;
/* eslint-disable @typescript-eslint/only-throw-error */
Object.defineProperty(exports, "__esModule", { value: true });
exports.jwtValidator = void 0;
const general_utils_1 = require("@villedemontreal/general-utils");
const jwt = require("jsonwebtoken");
const luxon_1 = require("luxon");
const constants_1 = require("./config/constants");
const customError_1 = require("./models/customError");
const jwtPayload_1 = require("./models/jwtPayload");
const publicKey_1 = require("./models/publicKey");
const cachedPublicKeyRepository_1 = require("./repositories/cachedPublicKeyRepository");
/**
* JWT Validator
*/
class JwtValidator {
async verifyAuthorizationHeader(header) {
if (general_utils_1.utils.isBlank(header)) {
throw (0, customError_1.createInvalidAuthHeaderError)({
code: constants_1.constants.errors.codes.NULL_VALUE,
target: 'Authorization header',
message: 'Empty Authorization header',
});
}
const parts = header.trim().split(' ');
if (parts[0] !== 'Bearer') {
throw (0, customError_1.createInvalidAuthHeaderError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'Authorization header',
message: 'Bad authentication scheme, "Bearer" required',
});
}
return await this.verifyToken(parts[1]);
}
async verifyToken(token) {
const payload = this.parseJwt(token);
const key = await this.getJwtPublicKey(payload);
this.validateJwtCreationTimestamp(payload, key);
this.validateJwtExpirationTimestamp(payload, key);
return this.verifyJwt(token, key);
}
parseJwt(token) {
const payload = jwt.decode(token);
if (!payload) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: 'jwt malformed',
});
}
return payload;
}
verifyJwt(token, publicKey) {
let payload;
try {
payload = jwt.verify(token, publicKey.parsedPublicKey ?? publicKey.publicKey);
}
catch (err) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: err.message,
});
}
if ((0, jwtPayload_1.isJWTPayload)(payload)) {
return payload;
}
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: 'expected a valid JWT payload',
});
}
async getJwtPublicKey(payload) {
const keyId = payload.keyId;
if (!keyId || keyId <= 0) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: 'missing public key ID',
});
}
const key = await cachedPublicKeyRepository_1.cachedPublicKeyRepository.getOne(keyId);
// Check key state
if (!key || key.state !== publicKey_1.PublicKeyState.ACTIVE) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: 'this keyId is no longer active',
});
}
return key;
}
validateJwtCreationTimestamp(payload, key) {
// Check the jwt was not created before the creation date of the key
const payloadIat = luxon_1.DateTime.fromMillis(payload.iat * 1000).toUTC();
const keyCreatedAt = luxon_1.DateTime.fromISO(key.createdAt).toUTC();
if (payloadIat < keyCreatedAt) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: "this jwt can't be created before the public key",
});
}
}
validateJwtExpirationTimestamp(payload, key) {
// Check expiration date
if (key.expiresAt) {
const keyexpiresAt = luxon_1.DateTime.fromISO(key.expiresAt).toUTC();
if (luxon_1.DateTime.utc() > keyexpiresAt) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: 'this keyId is expired',
});
}
// Check the jwt was not created after the expiration date of the key
const payloadIat = luxon_1.DateTime.fromMillis(payload.iat * 1000).toUTC();
if (payloadIat > keyexpiresAt) {
throw (0, customError_1.createInvalidJwtError)({
code: constants_1.constants.errors.codes.INVALID_VALUE,
target: 'jwt',
message: "this jwt can't be created after the expiration of the public key",
});
}
}
}
}
exports.jwtValidator = new JwtValidator();
//# sourceMappingURL=jwtValidator.js.map