UNPKG

@sourceloop/ctrl-plane-tenant-management-service

Version:

Tenant Management microservice for SaaS control plane

82 lines 3.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CallbackVerifierProvider = void 0; const tslib_1 = require("tslib"); const core_1 = require("@loopback/core"); const repository_1 = require("@loopback/repository"); const rest_1 = require("@loopback/rest"); const core_2 = require("@sourceloop/core"); const crypto_1 = require("crypto"); const loopback4_authentication_1 = require("loopback4-authentication"); const keys_1 = require("../keys"); const repositories_1 = require("../repositories"); const DEFAULT_TIME_TOLERANCE = 20000; let CallbackVerifierProvider = class CallbackVerifierProvider { constructor(logger, webhookSecretRepo, setCurrentUser, systemUser) { this.logger = logger; this.webhookSecretRepo = webhookSecretRepo; this.setCurrentUser = setCurrentUser; this.systemUser = systemUser; } value() { return this.intercept.bind(this); } async intercept(invocationCtx, next) { var _a; const { request } = invocationCtx.parent; const value = request.body; const TIMESTAMP_TOLERANCE = +DEFAULT_TIME_TOLERANCE; const timestamp = Number(request.headers['x-timestamp']); if (isNaN(timestamp)) { this.logger.error('Invalid timestamp'); throw new rest_1.HttpErrors.Unauthorized(); } const signature = request.headers['x-signature']; if (!signature || typeof signature !== 'string') { this.logger.error('Missing signature string'); throw new rest_1.HttpErrors.Unauthorized(); } const tenantId = (_a = value.tenant) === null || _a === void 0 ? void 0 : _a.id; if (!tenantId) { this.logger.error('Missing secret'); throw new rest_1.HttpErrors.Unauthorized(); } const secretInfo = await this.webhookSecretRepo.get(tenantId); if (!secretInfo) { this.logger.error('No secret found for this initiator'); throw new rest_1.HttpErrors.Unauthorized(); } const expectedSignature = (0, crypto_1.createHmac)('sha256', secretInfo.secret) .update(`${JSON.stringify(value)}${timestamp}`) .digest('hex'); try { // actual signature should be equal to expected signature // timing safe equal is used to prevent timing attacks if (!(0, crypto_1.timingSafeEqual)(Buffer.from(signature), Buffer.from(expectedSignature))) { this.logger.error('Invalid signature'); throw new rest_1.HttpErrors.Unauthorized(); } const hh = Math.abs(timestamp - Date.now()); // timestamp should be within 20 seconds if (hh > TIMESTAMP_TOLERANCE) { this.logger.error('Timestamp out of tolerance'); throw new rest_1.HttpErrors.Unauthorized(); } } catch (e) { this.logger.error(e); throw new rest_1.HttpErrors.Unauthorized(); } this.setCurrentUser(this.systemUser); return next(); } }; exports.CallbackVerifierProvider = CallbackVerifierProvider; exports.CallbackVerifierProvider = CallbackVerifierProvider = tslib_1.__decorate([ tslib_1.__param(0, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)), tslib_1.__param(1, (0, repository_1.repository)(repositories_1.WebhookSecretRepository)), tslib_1.__param(2, core_1.inject.setter(loopback4_authentication_1.AuthenticationBindings.CURRENT_USER)), tslib_1.__param(3, (0, core_1.inject)(keys_1.SYSTEM_USER)), tslib_1.__metadata("design:paramtypes", [Object, repositories_1.WebhookSecretRepository, Function, Object]) ], CallbackVerifierProvider); //# sourceMappingURL=callback-verifier.interceptor.js.map