UNPKG

@hookflo/tern

Version:

A robust, scalable webhook verification framework supporting multiple platforms and signature algorithms

175 lines (174 loc) 5.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.platformAlgorithmConfigs = void 0; exports.getPlatformAlgorithmConfig = getPlatformAlgorithmConfig; exports.platformUsesAlgorithm = platformUsesAlgorithm; exports.getPlatformsUsingAlgorithm = getPlatformsUsingAlgorithm; exports.validateSignatureConfig = validateSignatureConfig; exports.platformAlgorithmConfigs = { github: { platform: "github", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-hub-signature-256", headerFormat: "prefixed", prefix: "sha256=", timestampHeader: undefined, payloadFormat: "raw", }, description: "GitHub webhooks use HMAC-SHA256 with sha256= prefix", }, stripe: { platform: "stripe", signatureConfig: { algorithm: "hmac-sha256", headerName: "stripe-signature", headerFormat: "comma-separated", timestampHeader: undefined, payloadFormat: "timestamped", customConfig: { signatureFormat: "t={timestamp},v1={signature}", }, }, description: "Stripe webhooks use HMAC-SHA256 with comma-separated format", }, clerk: { platform: "clerk", signatureConfig: { algorithm: "hmac-sha256", headerName: "svix-signature", headerFormat: "raw", timestampHeader: "svix-timestamp", timestampFormat: "unix", payloadFormat: "custom", customConfig: { signatureFormat: "v1={signature}", payloadFormat: "{id}.{timestamp}.{body}", encoding: "base64", idHeader: "svix-id", }, }, description: "Clerk webhooks use HMAC-SHA256 with base64 encoding", }, dodopayments: { platform: "dodopayments", signatureConfig: { algorithm: "hmac-sha256", headerName: "webhook-signature", headerFormat: "raw", timestampHeader: "webhook-timestamp", timestampFormat: "unix", payloadFormat: "custom", customConfig: { signatureFormat: "v1={signature}", payloadFormat: "{id}.{timestamp}.{body}", encoding: "base64", idHeader: "webhook-id", }, }, description: "Dodo Payments webhooks use HMAC-SHA256 with svix-style format (Standard Webhooks)", }, shopify: { platform: "shopify", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-shopify-hmac-sha256", headerFormat: "raw", timestampHeader: "x-shopify-shop-domain", payloadFormat: "raw", }, description: "Shopify webhooks use HMAC-SHA256", }, vercel: { platform: "vercel", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-vercel-signature", headerFormat: "raw", timestampHeader: "x-vercel-timestamp", timestampFormat: "unix", payloadFormat: "raw", }, description: "Vercel webhooks use HMAC-SHA256", }, polar: { platform: "polar", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-polar-signature", headerFormat: "raw", timestampHeader: "x-polar-timestamp", timestampFormat: "unix", payloadFormat: "raw", }, description: "Polar webhooks use HMAC-SHA256", }, supabase: { platform: "supabase", signatureConfig: { algorithm: "custom", headerName: "x-webhook-token", headerFormat: "raw", payloadFormat: "raw", customConfig: { type: "token-based", idHeader: "x-webhook-id", }, }, description: "Supabase webhooks use token-based authentication", }, custom: { platform: "custom", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-webhook-token", headerFormat: "raw", payloadFormat: "raw", customConfig: { type: "token-based", idHeader: "x-webhook-id", }, }, description: "Custom webhook configuration", }, unknown: { platform: "unknown", signatureConfig: { algorithm: "hmac-sha256", headerName: "x-webhook-signature", headerFormat: "raw", payloadFormat: "raw", }, description: "Unknown platform - using default HMAC-SHA256", }, }; function getPlatformAlgorithmConfig(platform) { return exports.platformAlgorithmConfigs[platform] || exports.platformAlgorithmConfigs.unknown; } function platformUsesAlgorithm(platform, algorithm) { const config = getPlatformAlgorithmConfig(platform); return config.signatureConfig.algorithm === algorithm; } function getPlatformsUsingAlgorithm(algorithm) { return Object.entries(exports.platformAlgorithmConfigs) .filter(([_, config]) => config.signatureConfig.algorithm === algorithm) .map(([platform, _]) => platform); } function validateSignatureConfig(config) { if (!config.algorithm || !config.headerName) { return false; } switch (config.algorithm) { case "hmac-sha256": case "hmac-sha1": case "hmac-sha512": return true; case "rsa-sha256": case "ed25519": return !!config.customConfig?.publicKey; case "custom": return !!config.customConfig; default: return false; } }