@hookflo/tern 4.3.1-beta → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +28 -19
  2. package/dist/adapters/cloudflare.d.ts +1 -2
  3. package/dist/adapters/cloudflare.js +5 -1
  4. package/dist/adapters/express.d.ts +1 -2
  5. package/dist/adapters/express.js +5 -1
  6. package/dist/adapters/hono.d.ts +1 -2
  7. package/dist/adapters/hono.js +5 -1
  8. package/dist/adapters/nextjs.d.ts +1 -2
  9. package/dist/adapters/nextjs.js +5 -1
  10. package/dist/adapters/shared.js +5 -2
  11. package/dist/index.d.ts +4 -5
  12. package/dist/index.js +18 -24
  13. package/dist/platforms/algorithms.d.ts +20 -0
  14. package/dist/platforms/algorithms.js +65 -51
  15. package/dist/types.d.ts +7 -72
  16. package/dist/types.js +1 -2
  17. package/dist/verifiers/algorithms.d.ts +5 -1
  18. package/dist/verifiers/algorithms.js +46 -36
  19. package/package.json +1 -1
  20. package/dist/normalization/index.d.ts +0 -20
  21. package/dist/normalization/index.js +0 -78
  22. package/dist/normalization/providers/payment/paypal.d.ts +0 -2
  23. package/dist/normalization/providers/payment/paypal.js +0 -12
  24. package/dist/normalization/providers/payment/razorpay.d.ts +0 -2
  25. package/dist/normalization/providers/payment/razorpay.js +0 -13
  26. package/dist/normalization/providers/payment/stripe.d.ts +0 -2
  27. package/dist/normalization/providers/payment/stripe.js +0 -13
  28. package/dist/normalization/providers/registry.d.ts +0 -5
  29. package/dist/normalization/providers/registry.js +0 -21
  30. package/dist/normalization/simple.d.ts +0 -4
  31. package/dist/normalization/simple.js +0 -126
  32. package/dist/normalization/storage/interface.d.ts +0 -13
  33. package/dist/normalization/storage/interface.js +0 -2
  34. package/dist/normalization/storage/memory.d.ts +0 -12
  35. package/dist/normalization/storage/memory.js +0 -39
  36. package/dist/normalization/templates/base/auth.d.ts +0 -2
  37. package/dist/normalization/templates/base/auth.js +0 -22
  38. package/dist/normalization/templates/base/ecommerce.d.ts +0 -2
  39. package/dist/normalization/templates/base/ecommerce.js +0 -25
  40. package/dist/normalization/templates/base/payment.d.ts +0 -2
  41. package/dist/normalization/templates/base/payment.js +0 -25
  42. package/dist/normalization/templates/registry.d.ts +0 -6
  43. package/dist/normalization/templates/registry.js +0 -22
  44. package/dist/normalization/transformer/engine.d.ts +0 -11
  45. package/dist/normalization/transformer/engine.js +0 -86
  46. package/dist/normalization/transformer/validator.d.ts +0 -12
  47. package/dist/normalization/transformer/validator.js +0 -56
  48. package/dist/normalization/types.d.ts +0 -79
  49. package/dist/normalization/types.js +0 -2
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
8
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
9
9
 
10
- Stop writing webhook verification from scratch. **Tern** handles signature verification for Stripe, GitHub, Clerk, Shopify, and 15+ more platforms — with one consistent API.
10
+ Stop writing webhook verification from scratch. **Tern** handles signature verification for Stripe, GitHub, Clerk, Shopify, and 15+ more platforms — with one consistent API. It also verifies **Standard Webhooks** (including Svix-style `svix-*` and canonical `webhook-*` headers) through a single `standardwebhooks` platform config.
11
11
 
12
12
  > Need reliable delivery too? Tern supports inbound webhook delivery via Upstash QStash — automatic retries, DLQ management, replay controls, and Slack/Discord alerting. Bring your own Upstash account (BYOK).
13
13
 
@@ -171,6 +171,9 @@ app.post('/webhooks/stripe', createWebhookHandler({
171
171
 
172
172
  ## Supported Platforms
173
173
 
174
+ > ⚠️ Normalization is no longer supported in Tern and has been removed from the public verification APIs.
175
+
176
+
174
177
  | Platform | Algorithm | Status |
175
178
  |---|---|---|
176
179
  | **Stripe** | HMAC-SHA256 | ✅ Tested |
@@ -190,9 +193,8 @@ app.post('/webhooks/stripe', createWebhookHandler({
190
193
  | **Doppler** | HMAC-SHA256 | ✅ Tested |
191
194
  | **Sanity** | HMAC-SHA256 | ✅ Tested |
192
195
  | **Svix** | HMAC-SHA256 | ⚠️ Untested for now |
196
+ | **Standard Webhooks** (`standardwebhooks`) | HMAC-SHA256 | ✅ Tested |
193
197
  | **Linear** | HMAC-SHA256 | ⚠️ Untested for now |
194
- | **PagerDuty** | HMAC-SHA256 | ⚠️ Untested for now |
195
- | **Twilio** | HMAC-SHA1 | ⚠️ Untested for now |
196
198
  | **Razorpay** | HMAC-SHA256 | 🔄 Pending |
197
199
  | **Vercel** | HMAC-SHA256 | 🔄 Pending |
198
200
 
@@ -200,7 +202,7 @@ app.post('/webhooks/stripe', createWebhookHandler({
200
202
 
201
203
  ### Platform signature notes
202
204
 
203
- - **Standard Webhooks style** platforms (Clerk, Dodo Payments, Polar, ReplicateAI) commonly use a secret that starts with `whsec_...`.
205
+ - **Standard Webhooks style** providers are supported via the canonical `standardwebhooks` platform (with aliases for both `webhook-*` and `svix-*` headers). Clerk, Dodo Payments, Polar, and ReplicateAI all follow this pattern and commonly use a secret that starts with `whsec_...`.
204
206
  - **ReplicateAI**: copy the webhook signing secret from your Replicate webhook settings and pass it directly as `secret`.
205
207
  - **fal.ai**: supports JWKS key resolution out of the box — use `secret: ''` for auto key resolution, or pass a PEM public key explicitly.
206
208
 
@@ -341,25 +343,31 @@ const result = await WebhookVerificationService.verify(request, {
341
343
  });
342
344
  ```
343
345
 
344
- ### Svix / Standard Webhooks format (Clerk, Dodo Payments, ReplicateAI, etc.)
346
+ ### Standard Webhooks config helpers (Svix-style and webhook-* headers)
345
347
 
346
348
  ```typescript
347
- const svixConfig = {
348
- platform: 'my-svix-platform',
349
- secret: 'whsec_abc123...',
349
+ import {
350
+ createStandardWebhooksConfig,
351
+ STANDARD_WEBHOOKS_BASE,
352
+ } from '@hookflo/tern';
353
+
354
+ const signatureConfig = createStandardWebhooksConfig({
355
+ id: 'webhook-id',
356
+ timestamp: 'webhook-timestamp',
357
+ signature: 'webhook-signature',
358
+ idAliases: ['svix-id'],
359
+ timestampAliases: ['svix-timestamp'],
360
+ signatureAliases: ['svix-signature'],
361
+ });
362
+
363
+ const result = await WebhookVerificationService.verify(request, {
364
+ platform: 'standardwebhooks',
365
+ secret: process.env.STANDARD_WEBHOOKS_SECRET!,
350
366
  signatureConfig: {
351
- algorithm: 'hmac-sha256',
352
- headerName: 'webhook-signature',
353
- headerFormat: 'raw',
354
- timestampHeader: 'webhook-timestamp',
355
- timestampFormat: 'unix',
356
- payloadFormat: 'custom',
357
- customConfig: {
358
- payloadFormat: '{id}.{timestamp}.{body}',
359
- idHeader: 'webhook-id',
360
- },
367
+ ...STANDARD_WEBHOOKS_BASE,
368
+ ...signatureConfig,
361
369
  },
362
- };
370
+ });
363
371
  ```
364
372
 
365
373
  See the [SignatureConfig type](https://tern.hookflo.com) for all options.
@@ -407,6 +415,7 @@ interface WebhookVerificationResult {
407
415
 
408
416
  ## Troubleshooting
409
417
 
418
+
410
419
  **`Module not found: Can't resolve "@hookflo/tern/nextjs"`**
411
420
 
412
421
  ```bash
@@ -1,4 +1,4 @@
1
- import { WebhookPlatform, NormalizeOptions } from '../types';
1
+ import { WebhookPlatform } from '../types';
2
2
  import { QueueOption } from '../upstash/types';
3
3
  import type { AlertConfig, SendAlertOptions } from '../notifications/types';
4
4
  export interface CloudflareWebhookHandlerOptions<TEnv = Record<string, unknown>, TPayload = any, TMetadata extends Record<string, unknown> = Record<string, unknown>, TResponse = unknown> {
@@ -6,7 +6,6 @@ export interface CloudflareWebhookHandlerOptions<TEnv = Record<string, unknown>,
6
6
  secret?: string;
7
7
  secretEnv?: string;
8
8
  toleranceInSeconds?: number;
9
- normalize?: boolean | NormalizeOptions;
10
9
  queue?: QueueOption;
11
10
  alerts?: AlertConfig;
12
11
  alert?: Omit<SendAlertOptions, 'dlq' | 'dlqId' | 'source' | 'eventId'>;
@@ -39,7 +39,11 @@ function createWebhookHandler(options) {
39
39
  }
40
40
  return response;
41
41
  }
42
- const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, options.platform, secret, options.toleranceInSeconds, options.normalize);
42
+ const result = await index_1.WebhookVerificationService.verify(request, {
43
+ platform: options.platform,
44
+ secret,
45
+ toleranceInSeconds: options.toleranceInSeconds,
46
+ });
43
47
  if (!result.isValid) {
44
48
  return Response.json({ error: result.error, errorCode: result.errorCode, platform: result.platform, metadata: result.metadata }, { status: 400 });
45
49
  }
@@ -1,4 +1,4 @@
1
- import { WebhookPlatform, WebhookVerificationResult, NormalizeOptions } from '../types';
1
+ import { WebhookPlatform, WebhookVerificationResult } from '../types';
2
2
  import { QueueOption } from '../upstash/types';
3
3
  import { MinimalNodeRequest } from './shared';
4
4
  import type { AlertConfig, SendAlertOptions } from '../notifications/types';
@@ -14,7 +14,6 @@ export interface ExpressWebhookMiddlewareOptions {
14
14
  platform: WebhookPlatform;
15
15
  secret: string;
16
16
  toleranceInSeconds?: number;
17
- normalize?: boolean | NormalizeOptions;
18
17
  queue?: QueueOption;
19
18
  alerts?: AlertConfig;
20
19
  alert?: Omit<SendAlertOptions, 'dlq' | 'dlqId' | 'source' | 'eventId'>;
@@ -48,7 +48,11 @@ function createWebhookMiddleware(options) {
48
48
  }
49
49
  return;
50
50
  }
51
- const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(webRequest, options.platform, options.secret, options.toleranceInSeconds, options.normalize);
51
+ const result = await index_1.WebhookVerificationService.verify(webRequest, {
52
+ platform: options.platform,
53
+ secret: options.secret,
54
+ toleranceInSeconds: options.toleranceInSeconds,
55
+ });
52
56
  if (!result.isValid) {
53
57
  res.status(400).json({
54
58
  error: result.error,
@@ -1,4 +1,4 @@
1
- import { WebhookPlatform, NormalizeOptions } from '../types';
1
+ import { WebhookPlatform } from '../types';
2
2
  import { QueueOption } from '../upstash/types';
3
3
  import type { AlertConfig, SendAlertOptions } from '../notifications/types';
4
4
  export interface HonoContextLike {
@@ -11,7 +11,6 @@ export interface HonoWebhookHandlerOptions<TContext extends HonoContextLike = Ho
11
11
  platform: WebhookPlatform;
12
12
  secret: string;
13
13
  toleranceInSeconds?: number;
14
- normalize?: boolean | NormalizeOptions;
15
14
  queue?: QueueOption;
16
15
  alerts?: AlertConfig;
17
16
  alert?: Omit<SendAlertOptions, 'dlq' | 'dlqId' | 'source' | 'eventId'>;
@@ -35,7 +35,11 @@ function createWebhookHandler(options) {
35
35
  }
36
36
  return response;
37
37
  }
38
- const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, options.platform, options.secret, options.toleranceInSeconds, options.normalize);
38
+ const result = await index_1.WebhookVerificationService.verify(request, {
39
+ platform: options.platform,
40
+ secret: options.secret,
41
+ toleranceInSeconds: options.toleranceInSeconds,
42
+ });
39
43
  if (!result.isValid) {
40
44
  return c.json({
41
45
  error: result.error,
@@ -1,11 +1,10 @@
1
- import { WebhookPlatform, NormalizeOptions } from '../types';
1
+ import { WebhookPlatform } from '../types';
2
2
  import { QueueOption } from '../upstash/types';
3
3
  import type { AlertConfig, SendAlertOptions } from '../notifications/types';
4
4
  export interface NextWebhookHandlerOptions<TPayload = any, TMetadata extends Record<string, unknown> = Record<string, unknown>, TResponse = unknown> {
5
5
  platform: WebhookPlatform;
6
6
  secret: string;
7
7
  toleranceInSeconds?: number;
8
- normalize?: boolean | NormalizeOptions;
9
8
  queue?: QueueOption;
10
9
  alerts?: AlertConfig;
11
10
  alert?: Omit<SendAlertOptions, 'dlq' | 'dlqId' | 'source' | 'eventId'>;
@@ -34,7 +34,11 @@ function createWebhookHandler(options) {
34
34
  }
35
35
  return response;
36
36
  }
37
- const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, options.platform, options.secret, options.toleranceInSeconds, options.normalize);
37
+ const result = await index_1.WebhookVerificationService.verify(request, {
38
+ platform: options.platform,
39
+ secret: options.secret,
40
+ toleranceInSeconds: options.toleranceInSeconds,
41
+ });
38
42
  if (!result.isValid) {
39
43
  return Response.json({ error: result.error, errorCode: result.errorCode, platform: result.platform, metadata: result.metadata }, { status: 400 });
40
44
  }
@@ -90,8 +90,11 @@ function toHeadersInit(headers) {
90
90
  return normalized;
91
91
  }
92
92
  async function toWebRequest(request) {
93
- const protocol = request.protocol || 'https';
94
- const host = request.get?.('host')
93
+ const forwardedProto = getHeaderValue(request.headers, 'x-forwarded-proto')?.split(',')[0]?.trim();
94
+ const protocol = forwardedProto || request.protocol || 'https';
95
+ const forwardedHost = getHeaderValue(request.headers, 'x-forwarded-host')?.split(',')[0]?.trim();
96
+ const host = forwardedHost
97
+ || request.get?.('host')
95
98
  || getHeaderValue(request.headers, 'host')
96
99
  || 'localhost';
97
100
  const path = request.originalUrl || request.url || '/';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { WebhookConfig, WebhookVerificationResult, WebhookPlatform, SignatureConfig, MultiPlatformSecrets, NormalizeOptions } from './types';
1
+ import { WebhookConfig, WebhookVerificationResult, WebhookPlatform, SignatureConfig, MultiPlatformSecrets } from './types';
2
2
  import type { QueueOption } from './upstash/types';
3
3
  import type { AlertConfig, SendAlertOptions } from './notifications/types';
4
4
  export declare class WebhookVerificationService {
@@ -6,8 +6,8 @@ export declare class WebhookVerificationService {
6
6
  private static getVerifier;
7
7
  private static createAlgorithmBasedVerifier;
8
8
  private static getLegacyVerifier;
9
- static verifyWithPlatformConfig<TPayload = unknown>(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult<TPayload>>;
10
- static verifyAny<TPayload = unknown>(request: Request, secrets: MultiPlatformSecrets, toleranceInSeconds?: number, normalize?: boolean | NormalizeOptions): Promise<WebhookVerificationResult<TPayload>>;
9
+ static verifyWithPlatformConfig<TPayload = unknown>(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult<TPayload>>;
10
+ static verifyAny<TPayload = unknown>(request: Request, secrets: MultiPlatformSecrets, toleranceInSeconds?: number): Promise<WebhookVerificationResult<TPayload>>;
11
11
  private static resolveCanonicalEventId;
12
12
  private static safeCompare;
13
13
  private static pickString;
@@ -29,10 +29,9 @@ export declare class WebhookVerificationService {
29
29
  static verifyTokenBased<TPayload = unknown>(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult<TPayload>>;
30
30
  }
31
31
  export * from './types';
32
- export { getPlatformAlgorithmConfig, platformUsesAlgorithm, getPlatformsUsingAlgorithm, validateSignatureConfig, } from './platforms/algorithms';
32
+ export { getPlatformAlgorithmConfig, platformUsesAlgorithm, getPlatformsUsingAlgorithm, validateSignatureConfig, STANDARD_WEBHOOKS_BASE, createStandardWebhooksConfig, } from './platforms/algorithms';
33
33
  export { createAlgorithmVerifier } from './verifiers/algorithms';
34
34
  export { createCustomVerifier } from './verifiers/custom-algorithms';
35
- export { normalizePayload, getPlatformNormalizationCategory, getPlatformsByCategory, } from './normalization/simple';
36
35
  export * from './adapters';
37
36
  export * from './alerts';
38
37
  export default WebhookVerificationService;
package/dist/index.js CHANGED
@@ -36,12 +36,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getPlatformsByCategory = exports.getPlatformNormalizationCategory = exports.normalizePayload = exports.createCustomVerifier = exports.createAlgorithmVerifier = exports.validateSignatureConfig = exports.getPlatformsUsingAlgorithm = exports.platformUsesAlgorithm = exports.getPlatformAlgorithmConfig = exports.WebhookVerificationService = void 0;
39
+ exports.createCustomVerifier = exports.createAlgorithmVerifier = exports.createStandardWebhooksConfig = exports.STANDARD_WEBHOOKS_BASE = exports.validateSignatureConfig = exports.getPlatformsUsingAlgorithm = exports.platformUsesAlgorithm = exports.getPlatformAlgorithmConfig = exports.WebhookVerificationService = void 0;
40
40
  const crypto_1 = require("crypto");
41
41
  const algorithms_1 = require("./verifiers/algorithms");
42
42
  const custom_algorithms_1 = require("./verifiers/custom-algorithms");
43
43
  const algorithms_2 = require("./platforms/algorithms");
44
- const simple_1 = require("./normalization/simple");
45
44
  const dispatch_1 = require("./notifications/dispatch");
46
45
  class WebhookVerificationService {
47
46
  static async verify(request, config) {
@@ -51,9 +50,6 @@ class WebhookVerificationService {
51
50
  if (result.isValid) {
52
51
  result.platform = config.platform;
53
52
  result.eventId = this.resolveCanonicalEventId(config.platform, result.metadata, result.payload) ?? undefined;
54
- if (config.normalize) {
55
- result.payload = (0, simple_1.normalizePayload)(config.platform, result.payload, config.normalize);
56
- }
57
53
  }
58
54
  return result;
59
55
  }
@@ -70,12 +66,18 @@ class WebhookVerificationService {
70
66
  if (!signatureConfig) {
71
67
  throw new Error('Signature config is required for algorithm-based verification');
72
68
  }
69
+ const effectiveSignatureConfig = {
70
+ ...signatureConfig,
71
+ customConfig: {
72
+ ...(signatureConfig.customConfig || {}),
73
+ },
74
+ };
73
75
  // Use custom verifiers for special cases (token-based, etc.)
74
- if (signatureConfig.algorithm === 'custom') {
75
- return (0, custom_algorithms_1.createCustomVerifier)(secret, signatureConfig, toleranceInSeconds);
76
+ if (effectiveSignatureConfig.algorithm === 'custom') {
77
+ return (0, custom_algorithms_1.createCustomVerifier)(secret, effectiveSignatureConfig, toleranceInSeconds);
76
78
  }
77
79
  // Use algorithm-based verifiers for standard algorithms
78
- return (0, algorithms_1.createAlgorithmVerifier)(secret, signatureConfig, config.platform, toleranceInSeconds);
80
+ return (0, algorithms_1.createAlgorithmVerifier)(secret, effectiveSignatureConfig, config.platform, toleranceInSeconds);
79
81
  }
80
82
  static getLegacyVerifier(config) {
81
83
  // For legacy support, we'll use the algorithm-based approach
@@ -87,29 +89,28 @@ class WebhookVerificationService {
87
89
  return this.createAlgorithmBasedVerifier(configWithSignature);
88
90
  }
89
91
  // New method to create verifier using platform algorithm config
90
- static async verifyWithPlatformConfig(request, platform, secret, toleranceInSeconds = 300, normalize = false) {
92
+ static async verifyWithPlatformConfig(request, platform, secret, toleranceInSeconds = 300) {
91
93
  const platformConfig = (0, algorithms_2.getPlatformAlgorithmConfig)(platform);
92
94
  const config = {
93
95
  platform,
94
96
  secret,
95
97
  toleranceInSeconds,
96
- signatureConfig: platformConfig.signatureConfig,
97
- normalize,
98
+ signatureConfig: platformConfig.signatureConfig
98
99
  };
99
100
  return this.verify(request, config);
100
101
  }
101
- static async verifyAny(request, secrets, toleranceInSeconds = 300, normalize = false) {
102
+ static async verifyAny(request, secrets, toleranceInSeconds = 300) {
102
103
  const requestClone = request.clone();
103
104
  const detectedPlatform = this.detectPlatform(requestClone);
104
105
  if (detectedPlatform !== 'unknown' && secrets[detectedPlatform]) {
105
- return this.verifyWithPlatformConfig(requestClone, detectedPlatform, secrets[detectedPlatform], toleranceInSeconds, normalize);
106
+ return this.verifyWithPlatformConfig(requestClone, detectedPlatform, secrets[detectedPlatform], toleranceInSeconds);
106
107
  }
107
108
  const failedAttempts = [];
108
109
  const verificationResults = await Promise.all(Object.entries(secrets)
109
110
  .filter(([, secret]) => Boolean(secret))
110
111
  .map(async ([platform, secret]) => {
111
112
  const normalizedPlatform = platform.toLowerCase();
112
- const result = await this.verifyWithPlatformConfig(requestClone, normalizedPlatform, secret, toleranceInSeconds, normalize);
113
+ const result = await this.verifyWithPlatformConfig(requestClone, normalizedPlatform, secret, toleranceInSeconds);
113
114
  return {
114
115
  platform: normalizedPlatform,
115
116
  result,
@@ -190,9 +191,8 @@ class WebhookVerificationService {
190
191
  case 'sentry':
191
192
  case 'vercel':
192
193
  case 'linear':
193
- case 'pagerduty':
194
- case 'twilio':
195
194
  case 'svix':
195
+ case 'standardwebhooks':
196
196
  return this.pickString(payload?.id) || null;
197
197
  case 'doppler':
198
198
  return this.pickString(payload?.event?.id, metadata?.id) || null;
@@ -227,10 +227,6 @@ class WebhookVerificationService {
227
227
  return headers.has('svix-id') ? 'svix' : 'clerk';
228
228
  if (headers.has('linear-signature'))
229
229
  return 'linear';
230
- if (headers.has('x-pagerduty-signature'))
231
- return 'pagerduty';
232
- if (headers.has('x-twilio-signature'))
233
- return 'twilio';
234
230
  if (headers.has('workos-signature'))
235
231
  return 'workos';
236
232
  if (headers.has('webhook-signature')) {
@@ -371,14 +367,12 @@ Object.defineProperty(exports, "getPlatformAlgorithmConfig", { enumerable: true,
371
367
  Object.defineProperty(exports, "platformUsesAlgorithm", { enumerable: true, get: function () { return algorithms_3.platformUsesAlgorithm; } });
372
368
  Object.defineProperty(exports, "getPlatformsUsingAlgorithm", { enumerable: true, get: function () { return algorithms_3.getPlatformsUsingAlgorithm; } });
373
369
  Object.defineProperty(exports, "validateSignatureConfig", { enumerable: true, get: function () { return algorithms_3.validateSignatureConfig; } });
370
+ Object.defineProperty(exports, "STANDARD_WEBHOOKS_BASE", { enumerable: true, get: function () { return algorithms_3.STANDARD_WEBHOOKS_BASE; } });
371
+ Object.defineProperty(exports, "createStandardWebhooksConfig", { enumerable: true, get: function () { return algorithms_3.createStandardWebhooksConfig; } });
374
372
  var algorithms_4 = require("./verifiers/algorithms");
375
373
  Object.defineProperty(exports, "createAlgorithmVerifier", { enumerable: true, get: function () { return algorithms_4.createAlgorithmVerifier; } });
376
374
  var custom_algorithms_2 = require("./verifiers/custom-algorithms");
377
375
  Object.defineProperty(exports, "createCustomVerifier", { enumerable: true, get: function () { return custom_algorithms_2.createCustomVerifier; } });
378
- var simple_2 = require("./normalization/simple");
379
- Object.defineProperty(exports, "normalizePayload", { enumerable: true, get: function () { return simple_2.normalizePayload; } });
380
- Object.defineProperty(exports, "getPlatformNormalizationCategory", { enumerable: true, get: function () { return simple_2.getPlatformNormalizationCategory; } });
381
- Object.defineProperty(exports, "getPlatformsByCategory", { enumerable: true, get: function () { return simple_2.getPlatformsByCategory; } });
382
376
  __exportStar(require("./adapters"), exports);
383
377
  __exportStar(require("./alerts"), exports);
384
378
  exports.default = WebhookVerificationService;
@@ -1,4 +1,24 @@
1
1
  import { PlatformAlgorithmConfig, WebhookPlatform, SignatureConfig } from "../types";
2
+ export declare const STANDARD_WEBHOOKS_BASE: {
3
+ algorithm: "hmac-sha256";
4
+ headerFormat: "raw";
5
+ timestampFormat: "unix";
6
+ payloadFormat: "custom";
7
+ customConfig: {
8
+ signatureFormat: string;
9
+ payloadFormat: string;
10
+ encoding: string;
11
+ secretEncoding: string;
12
+ };
13
+ };
14
+ export declare function createStandardWebhooksConfig(headers: {
15
+ id: string;
16
+ timestamp: string;
17
+ signature: string;
18
+ idAliases?: string[];
19
+ timestampAliases?: string[];
20
+ signatureAliases?: string[];
21
+ }): SignatureConfig;
2
22
  export declare const platformAlgorithmConfigs: Record<WebhookPlatform, PlatformAlgorithmConfig>;
3
23
  export declare function getPlatformAlgorithmConfig(platform: WebhookPlatform): PlatformAlgorithmConfig;
4
24
  export declare function platformUsesAlgorithm(platform: WebhookPlatform, algorithm: string): boolean;
@@ -1,10 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.platformAlgorithmConfigs = void 0;
3
+ exports.platformAlgorithmConfigs = exports.STANDARD_WEBHOOKS_BASE = void 0;
4
+ exports.createStandardWebhooksConfig = createStandardWebhooksConfig;
4
5
  exports.getPlatformAlgorithmConfig = getPlatformAlgorithmConfig;
5
6
  exports.platformUsesAlgorithm = platformUsesAlgorithm;
6
7
  exports.getPlatformsUsingAlgorithm = getPlatformsUsingAlgorithm;
7
8
  exports.validateSignatureConfig = validateSignatureConfig;
9
+ exports.STANDARD_WEBHOOKS_BASE = {
10
+ algorithm: "hmac-sha256",
11
+ headerFormat: "raw",
12
+ timestampFormat: "unix",
13
+ payloadFormat: "custom",
14
+ customConfig: {
15
+ signatureFormat: "v1={signature}",
16
+ payloadFormat: "{id}.{timestamp}.{body}",
17
+ encoding: "base64",
18
+ secretEncoding: "base64",
19
+ },
20
+ };
21
+ function createStandardWebhooksConfig(headers) {
22
+ return {
23
+ ...exports.STANDARD_WEBHOOKS_BASE,
24
+ headerName: headers.signature,
25
+ timestampHeader: headers.timestamp,
26
+ customConfig: {
27
+ ...exports.STANDARD_WEBHOOKS_BASE.customConfig,
28
+ idHeader: headers.id,
29
+ ...(headers.idAliases && { idHeaderAliases: headers.idAliases }),
30
+ ...(headers.timestampAliases && {
31
+ timestampHeaderAliases: headers.timestampAliases,
32
+ }),
33
+ ...(headers.signatureAliases && {
34
+ signatureHeaderAliases: headers.signatureAliases,
35
+ }),
36
+ },
37
+ };
38
+ }
8
39
  exports.platformAlgorithmConfigs = {
9
40
  github: {
10
41
  platform: "github",
@@ -51,27 +82,6 @@ exports.platformAlgorithmConfigs = {
51
82
  },
52
83
  description: "Clerk webhooks use HMAC-SHA256 with base64 encoding",
53
84
  },
54
- svix: {
55
- platform: 'svix',
56
- signatureConfig: {
57
- algorithm: 'hmac-sha256',
58
- headerName: 'svix-signature',
59
- headerFormat: 'raw',
60
- timestampHeader: 'svix-timestamp',
61
- timestampFormat: 'unix',
62
- payloadFormat: 'custom',
63
- customConfig: {
64
- signatureFormat: 'v1={signature}',
65
- payloadFormat: '{id}.{timestamp}.{body}',
66
- encoding: 'base64',
67
- secretEncoding: 'base64',
68
- idHeader: 'svix-id',
69
- idHeaderAliases: ['webhook-id'],
70
- timestampHeaderAliases: ['webhook-timestamp'],
71
- },
72
- },
73
- description: 'Svix webhooks use HMAC-SHA256 with Standard Webhooks format',
74
- },
75
85
  dodopayments: {
76
86
  platform: "dodopayments",
77
87
  signatureConfig: {
@@ -309,48 +319,52 @@ exports.platformAlgorithmConfigs = {
309
319
  description: "Sanity webhooks use Stripe-compatible HMAC-SHA256 with base64 encoded signature and plain UTF-8 secret",
310
320
  },
311
321
  linear: {
312
- platform: 'linear',
322
+ platform: "linear",
313
323
  signatureConfig: {
314
- algorithm: 'hmac-sha256',
315
- headerName: 'linear-signature',
316
- headerFormat: 'raw',
317
- payloadFormat: 'raw',
324
+ algorithm: "hmac-sha256",
325
+ headerName: "linear-signature",
326
+ headerFormat: "raw",
327
+ payloadFormat: "raw",
318
328
  customConfig: {
319
329
  replayToleranceMs: 60000,
320
330
  },
321
331
  },
322
- description: 'Linear webhooks use HMAC-SHA256 on the raw body with a 60s timestamp replay window',
332
+ description: "Linear webhooks use HMAC-SHA256 on the raw body with a 60s timestamp replay window",
323
333
  },
324
- pagerduty: {
325
- platform: 'pagerduty',
334
+ svix: {
335
+ platform: "svix",
326
336
  signatureConfig: {
327
- algorithm: 'hmac-sha256',
328
- headerName: 'x-pagerduty-signature',
329
- headerFormat: 'raw',
330
- payloadFormat: 'raw',
331
- prefix: 'v1=',
337
+ algorithm: "hmac-sha256",
338
+ headerName: "svix-signature",
339
+ headerFormat: "raw",
340
+ timestampHeader: "svix-timestamp",
341
+ timestampFormat: "unix",
342
+ payloadFormat: "custom",
332
343
  customConfig: {
333
- signatureFormat: 'v1={signature}',
334
- comparePrefixed: true,
344
+ signatureFormat: "v1={signature}",
345
+ payloadFormat: "{id}.{timestamp}.{body}",
346
+ encoding: "base64",
347
+ secretEncoding: "base64",
348
+ idHeader: "svix-id",
349
+ idHeaderAliases: ["webhook-id"],
350
+ timestampHeaderAliases: ["webhook-timestamp"],
335
351
  },
336
352
  },
337
- description: 'PagerDuty webhooks use HMAC-SHA256 with v1=<hex> signatures',
353
+ description: "Svix webhooks use HMAC-SHA256 with Standard Webhooks format",
338
354
  },
339
- twilio: {
340
- platform: 'twilio',
355
+ standardwebhooks: {
356
+ platform: "standardwebhooks",
341
357
  signatureConfig: {
342
- algorithm: 'hmac-sha1',
343
- headerName: 'x-twilio-signature',
344
- headerFormat: 'raw',
345
- payloadFormat: 'custom',
346
- customConfig: {
347
- payloadFormat: '{url}',
348
- encoding: 'base64',
349
- secretEncoding: 'utf8',
350
- validateBodySHA256: true,
351
- },
358
+ ...createStandardWebhooksConfig({
359
+ id: "webhook-id",
360
+ timestamp: "webhook-timestamp",
361
+ signature: "webhook-signature",
362
+ idAliases: ["svix-id"],
363
+ timestampAliases: ["svix-timestamp"],
364
+ signatureAliases: ["svix-signature"],
365
+ }),
352
366
  },
353
- description: 'Twilio webhooks use HMAC-SHA1 with base64 signatures (URL canonicalization required)',
367
+ description: "Canonical Standard Webhooks implementation. Works for any platform using v1= HMAC-SHA256 signing regardless of header names.",
354
368
  },
355
369
  custom: {
356
370
  platform: "custom",