@hookflo/tern 1.0.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.
- package/LICENSE +21 -0
- package/README.md +396 -0
- package/dist/examples.d.ts +28 -0
- package/dist/examples.js +160 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +142 -0
- package/dist/platforms/algorithms.d.ts +6 -0
- package/dist/platforms/algorithms.js +180 -0
- package/dist/test-compiled.d.ts +1 -0
- package/dist/test-compiled.js +4 -0
- package/dist/test.d.ts +6 -0
- package/dist/test.js +102 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.js +16 -0
- package/dist/utils.d.ts +78 -0
- package/dist/utils.js +212 -0
- package/dist/verifiers/algorithms.d.ts +22 -0
- package/dist/verifiers/algorithms.js +273 -0
- package/dist/verifiers/base.d.ts +9 -0
- package/dist/verifiers/base.js +21 -0
- package/dist/verifiers/custom-algorithms.d.ts +18 -0
- package/dist/verifiers/custom-algorithms.js +279 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 tern
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# Tern
|
|
2
|
+
|
|
3
|
+
A robust, scalable webhook verification framework supporting multiple platforms and signature algorithms. Built with TypeScript for maximum type safety and developer experience.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Algorithm-based verification**: Instead of platform-specific verifiers, use algorithm-based verifiers
|
|
8
|
+
- **Platform configuration**: Each platform specifies which algorithm to use
|
|
9
|
+
- **Extensible framework**: Easy to add new algorithms and platforms
|
|
10
|
+
- **Most common algorithms**: HMAC-SHA256, HMAC-SHA1, HMAC-SHA512, and custom algorithms
|
|
11
|
+
- **TypeScript support**: Full type safety and IntelliSense
|
|
12
|
+
- **Zero dependencies**: Only uses Node.js built-in modules
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install tern
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Basic Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { WebhookVerificationService } from 'tern';
|
|
26
|
+
|
|
27
|
+
// Verify a GitHub webhook
|
|
28
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
29
|
+
request,
|
|
30
|
+
'github',
|
|
31
|
+
'your-github-secret',
|
|
32
|
+
300
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (result.isValid) {
|
|
36
|
+
console.log('Webhook verified successfully:', result.payload);
|
|
37
|
+
} else {
|
|
38
|
+
console.error('Verification failed:', result.error);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Token-based Authentication (Supabase, Custom)
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// For platforms that use simple token-based auth
|
|
46
|
+
const result = await WebhookVerificationService.verifyTokenBased(
|
|
47
|
+
request,
|
|
48
|
+
'your-webhook-id',
|
|
49
|
+
'your-webhook-token'
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Custom Signature Configuration
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { WebhookVerificationService } from 'tern';
|
|
57
|
+
|
|
58
|
+
const config = {
|
|
59
|
+
platform: 'custom',
|
|
60
|
+
secret: 'your-secret',
|
|
61
|
+
signatureConfig: {
|
|
62
|
+
algorithm: 'hmac-sha256',
|
|
63
|
+
headerName: 'x-signature',
|
|
64
|
+
headerFormat: 'prefixed',
|
|
65
|
+
prefix: 'sha256=',
|
|
66
|
+
payloadFormat: 'raw'
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const result = await WebhookVerificationService.verify(request, config);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 🏗️ Supported Platforms
|
|
74
|
+
|
|
75
|
+
### HMAC-SHA256 (Most Common)
|
|
76
|
+
- **GitHub**: `x-hub-signature-256` with `sha256=` prefix
|
|
77
|
+
- **Stripe**: `stripe-signature` with comma-separated format
|
|
78
|
+
- **Clerk**: `svix-signature` with base64 encoding
|
|
79
|
+
- **Dodo Payments**: `webhook-signature` with raw format
|
|
80
|
+
- **Shopify**: `x-shopify-hmac-sha256`
|
|
81
|
+
- **Vercel**: `x-vercel-signature`
|
|
82
|
+
- **Polar**: `x-polar-signature`
|
|
83
|
+
|
|
84
|
+
### Custom Algorithms
|
|
85
|
+
- **Supabase**: Token-based authentication
|
|
86
|
+
- **Clerk**: Custom base64 encoding
|
|
87
|
+
- **Stripe**: Custom comma-separated format
|
|
88
|
+
|
|
89
|
+
## 🔧 API Reference
|
|
90
|
+
|
|
91
|
+
### WebhookVerificationService
|
|
92
|
+
|
|
93
|
+
#### `verify(request: Request, config: WebhookConfig): Promise<WebhookVerificationResult>`
|
|
94
|
+
|
|
95
|
+
Verify a webhook using a configuration object.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const config = {
|
|
99
|
+
platform: 'github',
|
|
100
|
+
secret: 'your-secret',
|
|
101
|
+
toleranceInSeconds: 300
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = await WebhookVerificationService.verify(request, config);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### `verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>`
|
|
108
|
+
|
|
109
|
+
Verify a webhook using platform-specific configuration.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
113
|
+
request,
|
|
114
|
+
'stripe',
|
|
115
|
+
'your-stripe-secret',
|
|
116
|
+
300
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `verifyTokenBased(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult>`
|
|
121
|
+
|
|
122
|
+
Verify a webhook using simple token-based authentication.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const result = await WebhookVerificationService.verifyTokenBased(
|
|
126
|
+
request,
|
|
127
|
+
'your-webhook-id',
|
|
128
|
+
'your-webhook-token'
|
|
129
|
+
);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Utility Functions
|
|
133
|
+
|
|
134
|
+
#### `detectPlatformFromHeaders(headers: Headers): WebhookPlatform | null`
|
|
135
|
+
|
|
136
|
+
Automatically detect the platform from request headers.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import { detectPlatformFromHeaders } from 'tern';
|
|
140
|
+
|
|
141
|
+
const platform = detectPlatformFromHeaders(request.headers);
|
|
142
|
+
if (platform) {
|
|
143
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
144
|
+
request, platform, 'your-secret', 300
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### `getPlatformsUsingAlgorithm(algorithm: string): WebhookPlatform[]`
|
|
150
|
+
|
|
151
|
+
Get all platforms that use a specific algorithm.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { WebhookVerificationService } from 'tern';
|
|
155
|
+
|
|
156
|
+
const hmacPlatforms = WebhookVerificationService.getPlatformsUsingAlgorithm('hmac-sha256');
|
|
157
|
+
// Returns: ['github', 'stripe', 'clerk', 'dodopayments', ...]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 🎯 Usage Examples
|
|
161
|
+
|
|
162
|
+
### Express.js Integration
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import express from 'express';
|
|
166
|
+
import { WebhookVerificationService } from 'tern';
|
|
167
|
+
|
|
168
|
+
const app = express();
|
|
169
|
+
|
|
170
|
+
app.post('/webhook', async (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
173
|
+
req,
|
|
174
|
+
'github',
|
|
175
|
+
process.env.GITHUB_WEBHOOK_SECRET,
|
|
176
|
+
300
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (result.isValid) {
|
|
180
|
+
// Process the webhook
|
|
181
|
+
console.log('Webhook received:', result.payload);
|
|
182
|
+
res.status(200).json({ success: true });
|
|
183
|
+
} else {
|
|
184
|
+
res.status(401).json({ error: result.error });
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Next.js API Route
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// pages/api/webhook.ts
|
|
196
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
|
197
|
+
import { WebhookVerificationService } from 'tern';
|
|
198
|
+
|
|
199
|
+
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
200
|
+
if (req.method !== 'POST') {
|
|
201
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
206
|
+
req as any,
|
|
207
|
+
'stripe',
|
|
208
|
+
process.env.STRIPE_WEBHOOK_SECRET,
|
|
209
|
+
300
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
if (result.isValid) {
|
|
213
|
+
// Handle the webhook
|
|
214
|
+
console.log('Stripe webhook:', result.payload);
|
|
215
|
+
res.status(200).json({ received: true });
|
|
216
|
+
} else {
|
|
217
|
+
res.status(401).json({ error: result.error });
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Platform Detection
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { detectPlatformFromHeaders, WebhookVerificationService } from 'tern';
|
|
229
|
+
|
|
230
|
+
async function handleWebhook(request: Request) {
|
|
231
|
+
const platform = detectPlatformFromHeaders(request.headers);
|
|
232
|
+
|
|
233
|
+
if (!platform) {
|
|
234
|
+
throw new Error('Unknown webhook platform');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const secret = getSecretForPlatform(platform); // Your secret management
|
|
238
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
239
|
+
request,
|
|
240
|
+
platform,
|
|
241
|
+
secret,
|
|
242
|
+
300
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Custom Platform Integration
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { WebhookVerificationService } from 'tern';
|
|
253
|
+
|
|
254
|
+
const customConfig = {
|
|
255
|
+
platform: 'custom',
|
|
256
|
+
secret: 'your-custom-secret',
|
|
257
|
+
signatureConfig: {
|
|
258
|
+
algorithm: 'hmac-sha256',
|
|
259
|
+
headerName: 'x-custom-signature',
|
|
260
|
+
headerFormat: 'raw',
|
|
261
|
+
payloadFormat: 'raw'
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const result = await WebhookVerificationService.verify(request, customConfig);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## 🔧 Adding New Platforms
|
|
269
|
+
|
|
270
|
+
### Step 1: Add Platform Type
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// In your project, extend the types
|
|
274
|
+
declare module 'tern' {
|
|
275
|
+
interface WebhookPlatform {
|
|
276
|
+
'your-platform': 'your-platform';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Step 2: Use Custom Configuration
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const config = {
|
|
285
|
+
platform: 'custom',
|
|
286
|
+
secret: 'your-secret',
|
|
287
|
+
signatureConfig: {
|
|
288
|
+
algorithm: 'hmac-sha256', // or 'custom'
|
|
289
|
+
headerName: 'x-your-signature',
|
|
290
|
+
headerFormat: 'raw',
|
|
291
|
+
payloadFormat: 'raw'
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## 📊 Platform Support Matrix
|
|
297
|
+
|
|
298
|
+
| Platform | Algorithm | Header | Format |
|
|
299
|
+
|----------|-----------|--------|--------|
|
|
300
|
+
| GitHub | HMAC-SHA256 | `x-hub-signature-256` | `sha256=...` |
|
|
301
|
+
| Stripe | HMAC-SHA256 | `stripe-signature` | `t=...,v1=...` |
|
|
302
|
+
| Clerk | Custom | `svix-signature` | Base64 |
|
|
303
|
+
| Supabase | Token-based | `x-webhook-token` | Simple |
|
|
304
|
+
| Shopify | HMAC-SHA256 | `x-shopify-hmac-sha256` | Raw |
|
|
305
|
+
| Vercel | HMAC-SHA256 | `x-vercel-signature` | Raw |
|
|
306
|
+
| Polar | HMAC-SHA256 | `x-polar-signature` | Raw |
|
|
307
|
+
|
|
308
|
+
## 🔍 Error Handling
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
try {
|
|
312
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
313
|
+
request,
|
|
314
|
+
'github',
|
|
315
|
+
'your-secret',
|
|
316
|
+
300
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (!result.isValid) {
|
|
320
|
+
console.error('Verification failed:', result.error);
|
|
321
|
+
return res.status(401).json({ error: result.error });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Process webhook
|
|
325
|
+
console.log('Webhook verified:', result.payload);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('Verification error:', error);
|
|
328
|
+
return res.status(500).json({ error: 'Internal server error' });
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## 🧪 Testing
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { WebhookVerificationService } from 'tern';
|
|
336
|
+
|
|
337
|
+
// Create a mock request
|
|
338
|
+
const mockRequest = new Request('http://localhost/webhook', {
|
|
339
|
+
method: 'POST',
|
|
340
|
+
headers: {
|
|
341
|
+
'x-hub-signature-256': 'sha256=abc123',
|
|
342
|
+
'content-type': 'application/json'
|
|
343
|
+
},
|
|
344
|
+
body: JSON.stringify({ test: 'data' })
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const result = await WebhookVerificationService.verifyWithPlatformConfig(
|
|
348
|
+
mockRequest,
|
|
349
|
+
'github',
|
|
350
|
+
'test-secret',
|
|
351
|
+
300
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
console.log('Test result:', result);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## 📈 Performance
|
|
358
|
+
|
|
359
|
+
- **Zero dependencies**: Only uses Node.js built-in modules
|
|
360
|
+
- **Optimized algorithms**: Efficient HMAC verification
|
|
361
|
+
- **Timing-safe comparisons**: Prevents timing attacks
|
|
362
|
+
- **Minimal overhead**: Lightweight and fast
|
|
363
|
+
|
|
364
|
+
## 🔒 Security Features
|
|
365
|
+
|
|
366
|
+
- **Timing-safe comparisons**: Prevents timing attacks
|
|
367
|
+
- **Proper validation**: Comprehensive input validation
|
|
368
|
+
- **Secure defaults**: Secure by default configuration
|
|
369
|
+
- **Algorithm flexibility**: Support for multiple signature algorithms
|
|
370
|
+
|
|
371
|
+
## 🤝 Contributing
|
|
372
|
+
|
|
373
|
+
1. Fork the repository
|
|
374
|
+
2. Create a feature branch
|
|
375
|
+
3. Make your changes
|
|
376
|
+
4. Add tests
|
|
377
|
+
5. Submit a pull request
|
|
378
|
+
|
|
379
|
+
## 📄 License
|
|
380
|
+
|
|
381
|
+
MIT License - see LICENSE file for details.
|
|
382
|
+
|
|
383
|
+
## 🆘 Support
|
|
384
|
+
|
|
385
|
+
- **Issues**: [GitHub Issues](https://github.com/yourusername/tern/issues)
|
|
386
|
+
- **Documentation**: [GitHub Wiki](https://github.com/yourusername/tern/wiki)
|
|
387
|
+
- **Discussions**: [GitHub Discussions](https://github.com/yourusername/tern/discussions)
|
|
388
|
+
|
|
389
|
+
## 🚀 Roadmap
|
|
390
|
+
|
|
391
|
+
- [ ] RSA-SHA256 support
|
|
392
|
+
- [ ] Ed25519 support
|
|
393
|
+
- [ ] Performance optimizations
|
|
394
|
+
- [ ] More platform integrations
|
|
395
|
+
- [ ] Built-in rate limiting
|
|
396
|
+
- [ ] Monitoring and metrics
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function exampleBasicUsage(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
2
|
+
export declare function exampleTokenBased(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
3
|
+
export declare function exampleCustomSignature(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
4
|
+
export declare function examplePlatformDetection(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
5
|
+
export declare function exampleErrorHandling(request: Request): Promise<{
|
|
6
|
+
success: boolean;
|
|
7
|
+
error: string | undefined;
|
|
8
|
+
payload?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
success: boolean;
|
|
11
|
+
payload: any;
|
|
12
|
+
error?: undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function exampleBatchVerification(request: Request): Promise<({
|
|
15
|
+
platform: string;
|
|
16
|
+
success: boolean;
|
|
17
|
+
result: import("./types").WebhookVerificationResult;
|
|
18
|
+
error?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
platform: string;
|
|
21
|
+
success: boolean;
|
|
22
|
+
error: string;
|
|
23
|
+
result?: undefined;
|
|
24
|
+
})[]>;
|
|
25
|
+
export declare function exampleExpressIntegration(): any;
|
|
26
|
+
export declare function exampleNextJsApiRoute(): (req: any, res: any) => Promise<any>;
|
|
27
|
+
export declare function exampleCustomPlatform(request: Request): Promise<import("./types").WebhookVerificationResult>;
|
|
28
|
+
export declare function exampleHelperMethods(): void;
|
package/dist/examples.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.exampleBasicUsage = exampleBasicUsage;
|
|
4
|
+
exports.exampleTokenBased = exampleTokenBased;
|
|
5
|
+
exports.exampleCustomSignature = exampleCustomSignature;
|
|
6
|
+
exports.examplePlatformDetection = examplePlatformDetection;
|
|
7
|
+
exports.exampleErrorHandling = exampleErrorHandling;
|
|
8
|
+
exports.exampleBatchVerification = exampleBatchVerification;
|
|
9
|
+
exports.exampleExpressIntegration = exampleExpressIntegration;
|
|
10
|
+
exports.exampleNextJsApiRoute = exampleNextJsApiRoute;
|
|
11
|
+
exports.exampleCustomPlatform = exampleCustomPlatform;
|
|
12
|
+
exports.exampleHelperMethods = exampleHelperMethods;
|
|
13
|
+
const index_1 = require("./index");
|
|
14
|
+
// Example 1: Basic usage with platform config
|
|
15
|
+
async function exampleBasicUsage(request) {
|
|
16
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, 'github', 'your-github-secret', 300);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
// Example 2: Token-based authentication (Supabase style)
|
|
20
|
+
async function exampleTokenBased(request) {
|
|
21
|
+
const result = await index_1.WebhookVerificationService.verifyTokenBased(request, 'your-webhook-id', 'your-webhook-token');
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
// Example 3: Custom signature configuration
|
|
25
|
+
async function exampleCustomSignature(request) {
|
|
26
|
+
const signatureConfig = {
|
|
27
|
+
algorithm: 'hmac-sha256',
|
|
28
|
+
headerName: 'x-custom-signature',
|
|
29
|
+
headerFormat: 'prefixed',
|
|
30
|
+
prefix: 'sha256=',
|
|
31
|
+
payloadFormat: 'raw',
|
|
32
|
+
};
|
|
33
|
+
const config = {
|
|
34
|
+
platform: 'custom',
|
|
35
|
+
secret: 'your-custom-secret',
|
|
36
|
+
signatureConfig,
|
|
37
|
+
};
|
|
38
|
+
const result = await index_1.WebhookVerificationService.verify(request, config);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
// Example 4: Platform detection
|
|
42
|
+
async function examplePlatformDetection(request) {
|
|
43
|
+
const { detectPlatformFromHeaders } = require('./utils');
|
|
44
|
+
const platform = detectPlatformFromHeaders(request.headers);
|
|
45
|
+
if (platform) {
|
|
46
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, platform, 'your-secret', 300);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
throw new Error('Unknown platform');
|
|
50
|
+
}
|
|
51
|
+
// Example 5: Error handling
|
|
52
|
+
async function exampleErrorHandling(request) {
|
|
53
|
+
try {
|
|
54
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, 'github', 'your-secret', 300);
|
|
55
|
+
if (!result.isValid) {
|
|
56
|
+
console.error('Verification failed:', result.error);
|
|
57
|
+
return { success: false, error: result.error };
|
|
58
|
+
}
|
|
59
|
+
return { success: true, payload: result.payload };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('Verification error:', error);
|
|
63
|
+
return { success: false, error: error.message };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Example 6: Batch verification
|
|
67
|
+
async function exampleBatchVerification(request) {
|
|
68
|
+
const platforms = ['github', 'stripe', 'clerk'];
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const platform of platforms) {
|
|
71
|
+
try {
|
|
72
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, platform, 'your-secret', 300);
|
|
73
|
+
results.push({ platform, success: true, result });
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
results.push({ platform, success: false, error: error.message });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
// Example 7: Express.js integration
|
|
82
|
+
function exampleExpressIntegration() {
|
|
83
|
+
const express = require('express');
|
|
84
|
+
const app = express();
|
|
85
|
+
app.post('/webhook', async (req, res) => {
|
|
86
|
+
try {
|
|
87
|
+
const fetchRequest = new Request('http://localhost/webhook', {
|
|
88
|
+
method: req.method,
|
|
89
|
+
headers: req.headers, // headers need to be a plain object
|
|
90
|
+
body: JSON.stringify(req.body),
|
|
91
|
+
});
|
|
92
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(fetchRequest, 'github', process.env.GITHUB_WEBHOOK_SECRET || '', 300);
|
|
93
|
+
if (result.isValid) {
|
|
94
|
+
console.log('Webhook received:', result.payload);
|
|
95
|
+
res.status(200).json({ success: true });
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
res.status(401).json({ error: result.error });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return app;
|
|
106
|
+
}
|
|
107
|
+
// Example 8: Next.js API route
|
|
108
|
+
function exampleNextJsApiRoute() {
|
|
109
|
+
return async function handler(req, res) {
|
|
110
|
+
if (req.method !== 'POST') {
|
|
111
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(req, 'stripe', process.env.STRIPE_WEBHOOK_SECRET || '', 300);
|
|
115
|
+
if (result.isValid) {
|
|
116
|
+
console.log('Stripe webhook:', result.payload);
|
|
117
|
+
res.status(200).json({ received: true });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
res.status(401).json({ error: result.error });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Example 9: Custom platform integration
|
|
129
|
+
async function exampleCustomPlatform(request) {
|
|
130
|
+
const customConfig = {
|
|
131
|
+
platform: 'custom',
|
|
132
|
+
secret: 'your-custom-secret',
|
|
133
|
+
signatureConfig: {
|
|
134
|
+
algorithm: 'hmac-sha256',
|
|
135
|
+
headerName: 'x-custom-signature',
|
|
136
|
+
headerFormat: 'raw',
|
|
137
|
+
payloadFormat: 'raw',
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const result = await index_1.WebhookVerificationService.verify(request, customConfig);
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
// Example 10: Helper methods usage
|
|
144
|
+
function exampleHelperMethods() {
|
|
145
|
+
const { WebhookVerificationService } = require('./index');
|
|
146
|
+
// Get all platforms using HMAC-SHA256
|
|
147
|
+
const hmacPlatforms = WebhookVerificationService.getPlatformsUsingAlgorithm('hmac-sha256');
|
|
148
|
+
console.log('Platforms using HMAC-SHA256:', hmacPlatforms);
|
|
149
|
+
// Check if a platform uses a specific algorithm
|
|
150
|
+
const githubUsesHmac = WebhookVerificationService.platformUsesAlgorithm('github', 'hmac-sha256');
|
|
151
|
+
console.log('GitHub uses HMAC-SHA256:', githubUsesHmac);
|
|
152
|
+
// Validate a signature config
|
|
153
|
+
const config = {
|
|
154
|
+
algorithm: 'hmac-sha256',
|
|
155
|
+
headerName: 'x-signature',
|
|
156
|
+
payloadFormat: 'raw',
|
|
157
|
+
};
|
|
158
|
+
const isValid = WebhookVerificationService.validateSignatureConfig(config);
|
|
159
|
+
console.log('Config is valid:', isValid);
|
|
160
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WebhookConfig, WebhookVerificationResult, WebhookPlatform, SignatureConfig } from './types';
|
|
2
|
+
export declare class WebhookVerificationService {
|
|
3
|
+
static verify(request: Request, config: WebhookConfig): Promise<WebhookVerificationResult>;
|
|
4
|
+
private static getVerifier;
|
|
5
|
+
private static createAlgorithmBasedVerifier;
|
|
6
|
+
private static getLegacyVerifier;
|
|
7
|
+
static verifyWithPlatformConfig(request: Request, platform: WebhookPlatform, secret: string, toleranceInSeconds?: number): Promise<WebhookVerificationResult>;
|
|
8
|
+
static getPlatformsUsingAlgorithm(algorithm: string): WebhookPlatform[];
|
|
9
|
+
static platformUsesAlgorithm(platform: WebhookPlatform, algorithm: string): boolean;
|
|
10
|
+
static validateSignatureConfig(config: SignatureConfig): boolean;
|
|
11
|
+
static verifyTokenBased(request: Request, webhookId: string, webhookToken: string): Promise<WebhookVerificationResult>;
|
|
12
|
+
}
|
|
13
|
+
export * from './types';
|
|
14
|
+
export { getPlatformAlgorithmConfig, platformUsesAlgorithm, getPlatformsUsingAlgorithm, validateSignatureConfig, } from './platforms/algorithms';
|
|
15
|
+
export { createAlgorithmVerifier } from './verifiers/algorithms';
|
|
16
|
+
export { createCustomVerifier } from './verifiers/custom-algorithms';
|
|
17
|
+
export default WebhookVerificationService;
|