@hmcts/rpx-xui-node-lib
Version:
Common nodejs library components for XUI
126 lines • 5.67 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.s2s = exports.S2SAuth = void 0;
const events_1 = require("events");
const express_1 = require("express");
const jwt_decode_1 = __importDefault(require("jwt-decode"));
const otplib_1 = require("otplib");
const common_1 = require("../../common");
const s2s_constants_1 = require("./s2s.constants");
class S2SAuth extends events_1.EventEmitter {
constructor(logger = (0, common_1.getLogger)('auth:s2s')) {
super();
this.router = (0, express_1.Router)({ mergeParams: true });
this.s2sConfig = {
microservice: '',
s2sEndpointUrl: '',
s2sSecret: '',
};
// Cache of S2S tokens, indexed by microservice name
this.store = {};
/**
* This must be called with a suitable configuration before attempting to use the middleware, or else it will not
* have valid parameter values to generate the S2S token.
*
* @param s2sConfig The S2SConfig containing microservice name, S2S endpoint URL, and S2S secret
* @param store The cache for storing S2S tokens, indexed by microservice name
*/
this.configure = (s2sConfig, store) => {
this.s2sConfig = s2sConfig;
if (store) {
this.store = store;
}
this.router.use(this.s2sHandler);
return this.router;
};
this.s2sHandler = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
try {
const token = yield this.serviceTokenGenerator();
if (token) {
req.headers.ServiceAuthorization = `Bearer ${token}`;
// If there are no listeners for a success event from this emitter, just return this middleware using
// next(), else emit a success event with the S2S token
if (!this.listenerCount(s2s_constants_1.S2S.EVENT.AUTHENTICATE_SUCCESS)) {
return next();
}
else {
this.emit(s2s_constants_1.S2S.EVENT.AUTHENTICATE_SUCCESS, token, req, res, next);
return;
}
}
}
catch (error) {
next(error);
}
});
this.validateCache = () => {
const currentTime = Math.floor(Date.now() / 1000);
if (!this.store[this.s2sConfig.microservice]) {
return false;
}
return currentTime < this.store[this.s2sConfig.microservice].expiresAt;
};
this.getToken = () => {
return this.store[this.s2sConfig.microservice];
};
this.deleteCachedToken = () => {
if (this.store[this.s2sConfig.microservice]) {
delete this.store[this.s2sConfig.microservice];
}
};
this.generateToken = () => __awaiter(this, void 0, void 0, function* () {
this.logger.info('Generating new S2S token');
const token = yield this.postS2SLease();
const tokenData = (0, jwt_decode_1.default)(token);
this.store[this.s2sConfig.microservice] = {
expiresAt: tokenData.exp,
token,
};
return token;
});
this.postS2SLease = () => __awaiter(this, void 0, void 0, function* () {
const { s2sSecret, microservice, s2sEndpointUrl } = this.s2sConfig;
const secretBytes = new otplib_1.ScureBase32Plugin().decode(s2sSecret);
const guardrails = (0, otplib_1.createGuardrails)({ MIN_SECRET_BYTES: secretBytes.length });
const oneTimePassword = yield (0, otplib_1.generate)({ secret: s2sSecret, guardrails, strategy: 'totp' });
this.logger.info('Requesting S2S token for microservice:', microservice);
const request = yield common_1.http.post(`${s2sEndpointUrl}`, {
microservice,
oneTimePassword,
});
return request.data;
});
this.serviceTokenGenerator = () => __awaiter(this, void 0, void 0, function* () {
if (this.validateCache()) {
const tokenData = this.getToken();
return tokenData.token;
}
else {
return yield this.generateToken();
}
});
/**
* Get all the events that this strategy emits
* @return string[]
*/
this.getEvents = () => {
return Object.values(s2s_constants_1.S2S.EVENT);
};
this.logger = logger;
}
}
exports.S2SAuth = S2SAuth;
exports.s2s = new S2SAuth();
//# sourceMappingURL=s2s.class.js.map