@hookflo/tern 1.0.1 ā 1.0.2
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/README.md +191 -283
- package/dist/examples.d.ts +8 -28
- package/dist/examples.js +270 -128
- package/dist/index.js +8 -3
- package/dist/platforms/algorithms.js +2 -2
- package/dist/test-compiled.js +1 -1
- package/dist/test.d.ts +2 -6
- package/dist/test.js +160 -86
- package/dist/verifiers/algorithms.d.ts +17 -9
- package/dist/verifiers/algorithms.js +162 -139
- package/dist/verifiers/custom-algorithms.d.ts +0 -10
- package/dist/verifiers/custom-algorithms.js +7 -216
- package/package.json +1 -1
package/dist/test.js
CHANGED
|
@@ -1,102 +1,176 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
exports.testTokenBasedWebhook = testTokenBasedWebhook;
|
|
6
|
-
exports.testPlatformDetection = testPlatformDetection;
|
|
7
|
-
exports.testCustomSignature = testCustomSignature;
|
|
3
|
+
exports.runTests = runTests;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
8
5
|
const index_1 = require("./index");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
// Test data
|
|
7
|
+
const testSecret = 'whsec_test_secret_key_12345';
|
|
8
|
+
const testBody = JSON.stringify({ event: 'test', data: { id: '123' } });
|
|
9
|
+
// Helper function to create a mock request
|
|
10
|
+
function createMockRequest(headers, body = testBody) {
|
|
11
|
+
return new Request('https://example.com/webhook', {
|
|
13
12
|
method: 'POST',
|
|
14
13
|
headers,
|
|
15
14
|
body,
|
|
16
15
|
});
|
|
17
16
|
}
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
const result = await index_1.WebhookVerificationService.verifyWithPlatformConfig(request, 'github', 'test-secret', 300);
|
|
26
|
-
console.log('GitHub result:', result);
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
29
|
-
// Test token-based webhook (Supabase style)
|
|
30
|
-
async function testTokenBasedWebhook() {
|
|
31
|
-
console.log('Testing token-based webhook...');
|
|
32
|
-
const request = createMockRequest({
|
|
33
|
-
'x-webhook-id': 'test-webhook-id',
|
|
34
|
-
'x-webhook-token': 'test-webhook-token',
|
|
35
|
-
'content-type': 'application/json',
|
|
36
|
-
});
|
|
37
|
-
const result = await index_1.WebhookVerificationService.verifyTokenBased(request, 'test-webhook-id', 'test-webhook-token');
|
|
38
|
-
console.log('Token-based result:', result);
|
|
39
|
-
return result;
|
|
17
|
+
// Helper function to create Stripe signature
|
|
18
|
+
function createStripeSignature(body, secret, timestamp) {
|
|
19
|
+
const signedPayload = `${timestamp}.${body}`;
|
|
20
|
+
const hmac = (0, crypto_1.createHmac)('sha256', secret);
|
|
21
|
+
hmac.update(signedPayload);
|
|
22
|
+
const signature = hmac.digest('hex');
|
|
23
|
+
return `t=${timestamp},v1=${signature}`;
|
|
40
24
|
}
|
|
41
|
-
//
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
name: 'GitHub',
|
|
47
|
-
headers: { 'x-hub-signature-256': 'sha256=abc123' },
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'Stripe',
|
|
51
|
-
headers: { 'stripe-signature': 't=1234567890,v1=abc123' },
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'Clerk',
|
|
55
|
-
headers: { 'svix-signature': 'v1,abc123' },
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: 'Supabase',
|
|
59
|
-
headers: { 'x-webhook-token': 'token123' },
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
for (const testCase of testCases) {
|
|
63
|
-
const request = createMockRequest((0, utils_1.cleanHeaders)(testCase.headers));
|
|
64
|
-
const detectedPlatform = (0, utils_1.detectPlatformFromHeaders)(request.headers);
|
|
65
|
-
console.log(`${testCase.name}: ${detectedPlatform}`);
|
|
66
|
-
}
|
|
25
|
+
// Helper function to create GitHub signature
|
|
26
|
+
function createGitHubSignature(body, secret) {
|
|
27
|
+
const hmac = (0, crypto_1.createHmac)('sha256', secret);
|
|
28
|
+
hmac.update(body);
|
|
29
|
+
return `sha256=${hmac.digest('hex')}`;
|
|
67
30
|
}
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
const result = await index_1.WebhookVerificationService.verify(request, {
|
|
76
|
-
platform: 'custom',
|
|
77
|
-
secret: 'custom-secret',
|
|
78
|
-
signatureConfig: {
|
|
79
|
-
algorithm: 'hmac-sha256',
|
|
80
|
-
headerName: 'x-custom-signature',
|
|
81
|
-
headerFormat: 'prefixed',
|
|
82
|
-
prefix: 'sha256=',
|
|
83
|
-
payloadFormat: 'raw',
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
console.log('Custom signature result:', result);
|
|
87
|
-
return result;
|
|
31
|
+
// Helper function to create Clerk signature
|
|
32
|
+
function createClerkSignature(body, secret, id, timestamp) {
|
|
33
|
+
const signedContent = `${id}.${timestamp}.${body}`;
|
|
34
|
+
const secretBytes = new Uint8Array(Buffer.from(secret.split('_')[1], 'base64'));
|
|
35
|
+
const hmac = (0, crypto_1.createHmac)('sha256', secretBytes);
|
|
36
|
+
hmac.update(signedContent);
|
|
37
|
+
return `v1,${hmac.digest('base64')}`;
|
|
88
38
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
39
|
+
async function runTests() {
|
|
40
|
+
console.log('š§Ŗ Running Webhook Verification Tests...\n');
|
|
41
|
+
// Test 1: Stripe Webhook
|
|
42
|
+
console.log('1. Testing Stripe Webhook...');
|
|
43
|
+
try {
|
|
44
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
45
|
+
const stripeSignature = createStripeSignature(testBody, testSecret, timestamp);
|
|
46
|
+
const stripeRequest = createMockRequest({
|
|
47
|
+
'stripe-signature': stripeSignature,
|
|
48
|
+
'content-type': 'application/json',
|
|
49
|
+
});
|
|
50
|
+
const stripeResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(stripeRequest, 'stripe', testSecret);
|
|
51
|
+
console.log(' ā
Stripe:', stripeResult.isValid ? 'PASSED' : 'FAILED');
|
|
52
|
+
if (!stripeResult.isValid) {
|
|
53
|
+
console.log(' ā Error:', stripeResult.error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.log(' ā Stripe test failed:', error);
|
|
58
|
+
}
|
|
59
|
+
// Test 2: GitHub Webhook
|
|
60
|
+
console.log('\n2. Testing GitHub Webhook...');
|
|
61
|
+
try {
|
|
62
|
+
const githubSignature = createGitHubSignature(testBody, testSecret);
|
|
63
|
+
const githubRequest = createMockRequest({
|
|
64
|
+
'x-hub-signature-256': githubSignature,
|
|
65
|
+
'x-github-event': 'push',
|
|
66
|
+
'x-github-delivery': 'test-delivery-id',
|
|
67
|
+
'content-type': 'application/json',
|
|
68
|
+
});
|
|
69
|
+
const githubResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(githubRequest, 'github', testSecret);
|
|
70
|
+
console.log(' ā
GitHub:', githubResult.isValid ? 'PASSED' : 'FAILED');
|
|
71
|
+
if (!githubResult.isValid) {
|
|
72
|
+
console.log(' ā Error:', githubResult.error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.log(' ā GitHub test failed:', error);
|
|
77
|
+
}
|
|
78
|
+
// Test 3: Clerk Webhook
|
|
79
|
+
console.log('\n3. Testing Clerk Webhook...');
|
|
92
80
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
// Create a proper Clerk-style secret (whsec_ + base64 encoded secret)
|
|
82
|
+
const base64Secret = Buffer.from(testSecret).toString('base64');
|
|
83
|
+
const clerkSecret = `whsec_${base64Secret}`;
|
|
84
|
+
const id = 'test-id-123';
|
|
85
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
86
|
+
const clerkSignature = createClerkSignature(testBody, clerkSecret, id, timestamp);
|
|
87
|
+
const clerkRequest = createMockRequest({
|
|
88
|
+
'svix-signature': clerkSignature,
|
|
89
|
+
'svix-id': id,
|
|
90
|
+
'svix-timestamp': timestamp.toString(),
|
|
91
|
+
'content-type': 'application/json',
|
|
92
|
+
});
|
|
93
|
+
const clerkResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(clerkRequest, 'clerk', clerkSecret);
|
|
94
|
+
console.log(' ā
Clerk:', clerkResult.isValid ? 'PASSED' : 'FAILED');
|
|
95
|
+
if (!clerkResult.isValid) {
|
|
96
|
+
console.log(' ā Error:', clerkResult.error);
|
|
97
|
+
}
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
|
-
console.
|
|
100
|
+
console.log(' ā Clerk test failed:', error);
|
|
101
|
+
}
|
|
102
|
+
// Test 4: Standard HMAC-SHA256 (Generic)
|
|
103
|
+
console.log('\n4. Testing Standard HMAC-SHA256...');
|
|
104
|
+
try {
|
|
105
|
+
const hmac = (0, crypto_1.createHmac)('sha256', testSecret);
|
|
106
|
+
hmac.update(testBody);
|
|
107
|
+
const signature = hmac.digest('hex');
|
|
108
|
+
const genericRequest = createMockRequest({
|
|
109
|
+
'x-webhook-signature': signature,
|
|
110
|
+
'content-type': 'application/json',
|
|
111
|
+
});
|
|
112
|
+
const genericResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(genericRequest, 'unknown', testSecret);
|
|
113
|
+
console.log(' ā
Generic HMAC-SHA256:', genericResult.isValid ? 'PASSED' : 'FAILED');
|
|
114
|
+
if (!genericResult.isValid) {
|
|
115
|
+
console.log(' ā Error:', genericResult.error);
|
|
116
|
+
}
|
|
101
117
|
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.log(' ā Generic test failed:', error);
|
|
120
|
+
}
|
|
121
|
+
// Test 5: Token-based (Supabase)
|
|
122
|
+
console.log('\n5. Testing Token-based Authentication...');
|
|
123
|
+
try {
|
|
124
|
+
const webhookId = 'test-webhook-id';
|
|
125
|
+
const webhookToken = 'test-webhook-token';
|
|
126
|
+
const tokenRequest = createMockRequest({
|
|
127
|
+
'x-webhook-id': webhookId,
|
|
128
|
+
'x-webhook-token': webhookToken,
|
|
129
|
+
'content-type': 'application/json',
|
|
130
|
+
});
|
|
131
|
+
const tokenResult = await index_1.WebhookVerificationService.verifyTokenBased(tokenRequest, webhookId, webhookToken);
|
|
132
|
+
console.log(' ā
Token-based:', tokenResult.isValid ? 'PASSED' : 'FAILED');
|
|
133
|
+
if (!tokenResult.isValid) {
|
|
134
|
+
console.log(' ā Error:', tokenResult.error);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.log(' ā Token-based test failed:', error);
|
|
139
|
+
}
|
|
140
|
+
// Test 6: Invalid signatures
|
|
141
|
+
console.log('\n6. Testing Invalid Signatures...');
|
|
142
|
+
try {
|
|
143
|
+
const invalidRequest = createMockRequest({
|
|
144
|
+
'stripe-signature': 't=1234567890,v1=invalid_signature',
|
|
145
|
+
'content-type': 'application/json',
|
|
146
|
+
});
|
|
147
|
+
const invalidResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(invalidRequest, 'stripe', testSecret);
|
|
148
|
+
console.log(' ā
Invalid signature correctly rejected:', !invalidResult.isValid ? 'PASSED' : 'FAILED');
|
|
149
|
+
if (invalidResult.isValid) {
|
|
150
|
+
console.log(' ā Should have been rejected');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.log(' ā Invalid signature test failed:', error);
|
|
155
|
+
}
|
|
156
|
+
// Test 7: Missing headers
|
|
157
|
+
console.log('\n7. Testing Missing Headers...');
|
|
158
|
+
try {
|
|
159
|
+
const missingHeaderRequest = createMockRequest({
|
|
160
|
+
'content-type': 'application/json',
|
|
161
|
+
});
|
|
162
|
+
const missingHeaderResult = await index_1.WebhookVerificationService.verifyWithPlatformConfig(missingHeaderRequest, 'stripe', testSecret);
|
|
163
|
+
console.log(' ā
Missing headers correctly rejected:', !missingHeaderResult.isValid ? 'PASSED' : 'FAILED');
|
|
164
|
+
if (missingHeaderResult.isValid) {
|
|
165
|
+
console.log(' ā Should have been rejected');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.log(' ā Missing headers test failed:', error);
|
|
170
|
+
}
|
|
171
|
+
console.log('\nš All tests completed!');
|
|
172
|
+
}
|
|
173
|
+
// Run tests if this file is executed directly
|
|
174
|
+
if (require.main === module) {
|
|
175
|
+
runTests().catch(console.error);
|
|
102
176
|
}
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import { WebhookVerifier } from "./base";
|
|
2
|
-
import { WebhookVerificationResult, SignatureConfig } from "../types";
|
|
2
|
+
import { WebhookVerificationResult, SignatureConfig, WebhookPlatform } from "../types";
|
|
3
3
|
export declare abstract class AlgorithmBasedVerifier extends WebhookVerifier {
|
|
4
4
|
protected config: SignatureConfig;
|
|
5
|
-
|
|
5
|
+
protected platform: WebhookPlatform;
|
|
6
|
+
constructor(secret: string, config: SignatureConfig, platform: WebhookPlatform, toleranceInSeconds?: number);
|
|
6
7
|
abstract verify(request: Request): Promise<WebhookVerificationResult>;
|
|
7
8
|
protected extractSignature(request: Request): string | null;
|
|
8
9
|
protected extractTimestamp(request: Request): number | null;
|
|
9
|
-
protected
|
|
10
|
+
protected extractTimestampFromSignature(request: Request): number | null;
|
|
11
|
+
protected formatPayload(rawBody: string, request: Request): string;
|
|
12
|
+
protected formatCustomPayload(rawBody: string, request: Request): string;
|
|
10
13
|
protected verifyHMAC(payload: string, signature: string, algorithm?: string): boolean;
|
|
11
14
|
protected verifyHMACWithPrefix(payload: string, signature: string, algorithm?: string): boolean;
|
|
15
|
+
protected verifyHMACWithBase64(payload: string, signature: string, algorithm?: string): boolean;
|
|
16
|
+
protected extractMetadata(request: Request): Record<string, any>;
|
|
12
17
|
}
|
|
13
|
-
export declare class
|
|
18
|
+
export declare class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
14
19
|
verify(request: Request): Promise<WebhookVerificationResult>;
|
|
15
20
|
}
|
|
16
|
-
export declare class
|
|
17
|
-
|
|
21
|
+
export declare class HMACSHA256Verifier extends GenericHMACVerifier {
|
|
22
|
+
constructor(secret: string, config: SignatureConfig, platform?: WebhookPlatform, toleranceInSeconds?: number);
|
|
18
23
|
}
|
|
19
|
-
export declare class
|
|
20
|
-
|
|
24
|
+
export declare class HMACSHA1Verifier extends GenericHMACVerifier {
|
|
25
|
+
constructor(secret: string, config: SignatureConfig, platform?: WebhookPlatform, toleranceInSeconds?: number);
|
|
26
|
+
}
|
|
27
|
+
export declare class HMACSHA512Verifier extends GenericHMACVerifier {
|
|
28
|
+
constructor(secret: string, config: SignatureConfig, platform?: WebhookPlatform, toleranceInSeconds?: number);
|
|
21
29
|
}
|
|
22
|
-
export declare function createAlgorithmVerifier(secret: string, config: SignatureConfig, toleranceInSeconds?: number): AlgorithmBasedVerifier;
|
|
30
|
+
export declare function createAlgorithmVerifier(secret: string, config: SignatureConfig, platform?: WebhookPlatform, toleranceInSeconds?: number): AlgorithmBasedVerifier;
|