@hookflo/tern
Version:
A robust, scalable webhook verification framework supporting multiple platforms and signature algorithms
148 lines (147 loc) • 7.26 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCustomVerifier = exports.createAlgorithmVerifier = exports.validateSignatureConfig = exports.getPlatformsUsingAlgorithm = exports.platformUsesAlgorithm = exports.getPlatformAlgorithmConfig = exports.WebhookVerificationService = void 0;
const algorithms_1 = require("./verifiers/algorithms");
const custom_algorithms_1 = require("./verifiers/custom-algorithms");
const algorithms_2 = require("./platforms/algorithms");
class WebhookVerificationService {
static async verify(request, config) {
const verifier = this.getVerifier(config);
const result = await verifier.verify(request);
// Ensure the platform is set correctly in the result
if (result.isValid) {
result.platform = config.platform;
}
return result;
}
static getVerifier(config) {
const platform = config.platform.toLowerCase();
// If a custom signature config is provided, use the new algorithm-based framework
if (config.signatureConfig) {
return this.createAlgorithmBasedVerifier(config);
}
// Fallback to platform-specific verifiers for backward compatibility
return this.getLegacyVerifier(config);
}
static createAlgorithmBasedVerifier(config) {
const { signatureConfig, secret, toleranceInSeconds = 300 } = config;
if (!signatureConfig) {
throw new Error('Signature config is required for algorithm-based verification');
}
// Use custom verifiers for special cases (token-based, etc.)
if (signatureConfig.algorithm === 'custom') {
return (0, custom_algorithms_1.createCustomVerifier)(secret, signatureConfig, toleranceInSeconds);
}
// Use algorithm-based verifiers for standard algorithms
return (0, algorithms_1.createAlgorithmVerifier)(secret, signatureConfig, config.platform, toleranceInSeconds);
}
static getLegacyVerifier(config) {
const platform = config.platform.toLowerCase();
// For legacy support, we'll use the algorithm-based approach
const platformConfig = (0, algorithms_2.getPlatformAlgorithmConfig)(platform);
const configWithSignature = {
...config,
signatureConfig: platformConfig.signatureConfig,
};
return this.createAlgorithmBasedVerifier(configWithSignature);
}
// New method to create verifier using platform algorithm config
static async verifyWithPlatformConfig(request, platform, secret, toleranceInSeconds = 300) {
const platformConfig = (0, algorithms_2.getPlatformAlgorithmConfig)(platform);
const config = {
platform,
secret,
toleranceInSeconds,
signatureConfig: platformConfig.signatureConfig,
};
return await this.verify(request, config);
}
// Helper method to get all platforms using a specific algorithm
static getPlatformsUsingAlgorithm(algorithm) {
const { getPlatformsUsingAlgorithm } = require('./platforms/algorithms');
return getPlatformsUsingAlgorithm(algorithm);
}
// Helper method to check if a platform uses a specific algorithm
static platformUsesAlgorithm(platform, algorithm) {
const { platformUsesAlgorithm } = require('./platforms/algorithms');
return platformUsesAlgorithm(platform, algorithm);
}
// Helper method to validate signature config
static validateSignatureConfig(config) {
const { validateSignatureConfig } = require('./platforms/algorithms');
return validateSignatureConfig(config);
}
// Simple token-based verification for platforms like Supabase
static async verifyTokenBased(request, webhookId, webhookToken) {
try {
const idHeader = request.headers.get('x-webhook-id');
const tokenHeader = request.headers.get('x-webhook-token');
if (!idHeader || !tokenHeader) {
return {
isValid: false,
error: 'Missing required headers: x-webhook-id and x-webhook-token',
platform: 'custom',
};
}
// Simple comparison - we don't actually verify, just check if tokens match
const isValid = idHeader === webhookId && tokenHeader === webhookToken;
if (!isValid) {
return {
isValid: false,
error: 'Invalid webhook ID or token',
platform: 'custom',
};
}
const rawBody = await request.text();
let payload;
try {
payload = JSON.parse(rawBody);
}
catch (e) {
payload = rawBody;
}
return {
isValid: true,
platform: 'custom',
payload,
metadata: {
id: idHeader,
algorithm: 'token-based',
},
};
}
catch (error) {
return {
isValid: false,
error: `Token-based verification error: ${error.message}`,
platform: 'custom',
};
}
}
}
exports.WebhookVerificationService = WebhookVerificationService;
__exportStar(require("./types"), exports);
var algorithms_3 = require("./platforms/algorithms");
Object.defineProperty(exports, "getPlatformAlgorithmConfig", { enumerable: true, get: function () { return algorithms_3.getPlatformAlgorithmConfig; } });
Object.defineProperty(exports, "platformUsesAlgorithm", { enumerable: true, get: function () { return algorithms_3.platformUsesAlgorithm; } });
Object.defineProperty(exports, "getPlatformsUsingAlgorithm", { enumerable: true, get: function () { return algorithms_3.getPlatformsUsingAlgorithm; } });
Object.defineProperty(exports, "validateSignatureConfig", { enumerable: true, get: function () { return algorithms_3.validateSignatureConfig; } });
var algorithms_4 = require("./verifiers/algorithms");
Object.defineProperty(exports, "createAlgorithmVerifier", { enumerable: true, get: function () { return algorithms_4.createAlgorithmVerifier; } });
var custom_algorithms_2 = require("./verifiers/custom-algorithms");
Object.defineProperty(exports, "createCustomVerifier", { enumerable: true, get: function () { return custom_algorithms_2.createCustomVerifier; } });
exports.default = WebhookVerificationService;