@hookflo/tern 1.0.0 → 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
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HMACSHA512Verifier = exports.HMACSHA1Verifier = exports.HMACSHA256Verifier = exports.AlgorithmBasedVerifier = void 0;
|
|
3
|
+
exports.HMACSHA512Verifier = exports.HMACSHA1Verifier = exports.HMACSHA256Verifier = exports.GenericHMACVerifier = exports.AlgorithmBasedVerifier = void 0;
|
|
4
4
|
exports.createAlgorithmVerifier = createAlgorithmVerifier;
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
6
|
const base_1 = require("./base");
|
|
7
7
|
class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
8
|
-
constructor(secret, config, toleranceInSeconds = 300) {
|
|
8
|
+
constructor(secret, config, platform, toleranceInSeconds = 300) {
|
|
9
9
|
super(secret, toleranceInSeconds);
|
|
10
10
|
this.config = config;
|
|
11
|
+
this.platform = platform;
|
|
11
12
|
}
|
|
12
13
|
extractSignature(request) {
|
|
13
14
|
const headerValue = request.headers.get(this.config.headerName);
|
|
@@ -15,9 +16,8 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
15
16
|
return null;
|
|
16
17
|
switch (this.config.headerFormat) {
|
|
17
18
|
case "prefixed":
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
: null;
|
|
19
|
+
// For GitHub, return the full signature including prefix for comparison
|
|
20
|
+
return headerValue;
|
|
21
21
|
case "comma-separated":
|
|
22
22
|
// Handle comma-separated format like Stripe: "t=1234567890,v1=abc123"
|
|
23
23
|
const parts = headerValue.split(",");
|
|
@@ -31,6 +31,17 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
31
31
|
return sigMap.v1 || sigMap.signature || null;
|
|
32
32
|
case "raw":
|
|
33
33
|
default:
|
|
34
|
+
// For Clerk, handle space-separated signatures
|
|
35
|
+
if (this.platform === "clerk") {
|
|
36
|
+
const signatures = headerValue.split(" ");
|
|
37
|
+
for (const sig of signatures) {
|
|
38
|
+
const [version, signature] = sig.split(",");
|
|
39
|
+
if (version === "v1") {
|
|
40
|
+
return signature;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
34
45
|
return headerValue;
|
|
35
46
|
}
|
|
36
47
|
}
|
|
@@ -52,18 +63,62 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
52
63
|
return parseInt(timestampHeader, 10);
|
|
53
64
|
}
|
|
54
65
|
}
|
|
55
|
-
|
|
66
|
+
extractTimestampFromSignature(request) {
|
|
67
|
+
// For platforms like Stripe where timestamp is embedded in signature
|
|
68
|
+
if (this.config.headerFormat === "comma-separated") {
|
|
69
|
+
const headerValue = request.headers.get(this.config.headerName);
|
|
70
|
+
if (!headerValue)
|
|
71
|
+
return null;
|
|
72
|
+
const parts = headerValue.split(",");
|
|
73
|
+
const sigMap = {};
|
|
74
|
+
for (const part of parts) {
|
|
75
|
+
const [key, value] = part.split("=");
|
|
76
|
+
if (key && value) {
|
|
77
|
+
sigMap[key] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return sigMap.t ? parseInt(sigMap.t, 10) : null;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
formatPayload(rawBody, request) {
|
|
56
85
|
switch (this.config.payloadFormat) {
|
|
57
86
|
case "timestamped":
|
|
87
|
+
// For Stripe, timestamp is embedded in signature
|
|
88
|
+
const timestamp = this.extractTimestampFromSignature(request) ||
|
|
89
|
+
this.extractTimestamp(request);
|
|
58
90
|
return timestamp ? `${timestamp}.${rawBody}` : rawBody;
|
|
59
91
|
case "custom":
|
|
60
|
-
|
|
61
|
-
return rawBody;
|
|
92
|
+
return this.formatCustomPayload(rawBody, request);
|
|
62
93
|
case "raw":
|
|
63
94
|
default:
|
|
64
95
|
return rawBody;
|
|
65
96
|
}
|
|
66
97
|
}
|
|
98
|
+
formatCustomPayload(rawBody, request) {
|
|
99
|
+
if (!this.config.customConfig?.payloadFormat) {
|
|
100
|
+
return rawBody;
|
|
101
|
+
}
|
|
102
|
+
const customFormat = this.config.customConfig.payloadFormat;
|
|
103
|
+
// Handle Clerk-style format: {id}.{timestamp}.{body}
|
|
104
|
+
if (customFormat.includes("{id}") && customFormat.includes("{timestamp}")) {
|
|
105
|
+
const id = request.headers.get(this.config.customConfig.idHeader || "x-webhook-id");
|
|
106
|
+
const timestamp = request.headers.get(this.config.timestampHeader || "x-webhook-timestamp");
|
|
107
|
+
return customFormat
|
|
108
|
+
.replace("{id}", id || "")
|
|
109
|
+
.replace("{timestamp}", timestamp || "")
|
|
110
|
+
.replace("{body}", rawBody);
|
|
111
|
+
}
|
|
112
|
+
// Handle Stripe-style format: {timestamp}.{body}
|
|
113
|
+
if (customFormat.includes("{timestamp}") &&
|
|
114
|
+
customFormat.includes("{body}")) {
|
|
115
|
+
const timestamp = this.extractTimestamp(request);
|
|
116
|
+
return customFormat
|
|
117
|
+
.replace("{timestamp}", timestamp?.toString() || "")
|
|
118
|
+
.replace("{body}", rawBody);
|
|
119
|
+
}
|
|
120
|
+
return rawBody;
|
|
121
|
+
}
|
|
67
122
|
verifyHMAC(payload, signature, algorithm = "sha256") {
|
|
68
123
|
const hmac = (0, crypto_1.createHmac)(algorithm, this.secret);
|
|
69
124
|
hmac.update(payload);
|
|
@@ -76,9 +131,54 @@ class AlgorithmBasedVerifier extends base_1.WebhookVerifier {
|
|
|
76
131
|
const expectedSignature = `${this.config.prefix || ""}${hmac.digest("hex")}`;
|
|
77
132
|
return this.safeCompare(signature, expectedSignature);
|
|
78
133
|
}
|
|
134
|
+
verifyHMACWithBase64(payload, signature, algorithm = "sha256") {
|
|
135
|
+
// For platforms like Clerk that use base64 encoding
|
|
136
|
+
const secretBytes = new Uint8Array(Buffer.from(this.secret.split("_")[1], "base64"));
|
|
137
|
+
const hmac = (0, crypto_1.createHmac)(algorithm, secretBytes);
|
|
138
|
+
hmac.update(payload);
|
|
139
|
+
const expectedSignature = hmac.digest("base64");
|
|
140
|
+
return this.safeCompare(signature, expectedSignature);
|
|
141
|
+
}
|
|
142
|
+
extractMetadata(request) {
|
|
143
|
+
const metadata = {
|
|
144
|
+
algorithm: this.config.algorithm,
|
|
145
|
+
};
|
|
146
|
+
// Add timestamp if available
|
|
147
|
+
const timestamp = this.extractTimestamp(request);
|
|
148
|
+
if (timestamp) {
|
|
149
|
+
metadata.timestamp = timestamp.toString();
|
|
150
|
+
}
|
|
151
|
+
// Add platform-specific metadata
|
|
152
|
+
switch (this.platform) {
|
|
153
|
+
case "github":
|
|
154
|
+
metadata.event = request.headers.get("x-github-event");
|
|
155
|
+
metadata.delivery = request.headers.get("x-github-delivery");
|
|
156
|
+
break;
|
|
157
|
+
case "stripe":
|
|
158
|
+
// Extract Stripe-specific metadata from signature
|
|
159
|
+
const headerValue = request.headers.get(this.config.headerName);
|
|
160
|
+
if (headerValue && this.config.headerFormat === "comma-separated") {
|
|
161
|
+
const parts = headerValue.split(",");
|
|
162
|
+
const sigMap = {};
|
|
163
|
+
for (const part of parts) {
|
|
164
|
+
const [key, value] = part.split("=");
|
|
165
|
+
if (key && value) {
|
|
166
|
+
sigMap[key] = value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
metadata.id = sigMap.id;
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
case "clerk":
|
|
173
|
+
metadata.id = request.headers.get("svix-id");
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
return metadata;
|
|
177
|
+
}
|
|
79
178
|
}
|
|
80
179
|
exports.AlgorithmBasedVerifier = AlgorithmBasedVerifier;
|
|
81
|
-
|
|
180
|
+
// Generic HMAC Verifier that handles all HMAC-based algorithms
|
|
181
|
+
class GenericHMACVerifier extends AlgorithmBasedVerifier {
|
|
82
182
|
async verify(request) {
|
|
83
183
|
try {
|
|
84
184
|
const signature = this.extractSignature(request);
|
|
@@ -86,88 +186,53 @@ class HMACSHA256Verifier extends AlgorithmBasedVerifier {
|
|
|
86
186
|
return {
|
|
87
187
|
isValid: false,
|
|
88
188
|
error: `Missing signature header: ${this.config.headerName}`,
|
|
89
|
-
platform:
|
|
189
|
+
platform: this.platform,
|
|
90
190
|
};
|
|
91
191
|
}
|
|
92
192
|
const rawBody = await request.text();
|
|
93
|
-
|
|
193
|
+
// Extract timestamp based on platform configuration
|
|
194
|
+
let timestamp = null;
|
|
195
|
+
if (this.config.headerFormat === "comma-separated") {
|
|
196
|
+
// For platforms like Stripe where timestamp is embedded in signature
|
|
197
|
+
timestamp = this.extractTimestampFromSignature(request);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// For platforms with separate timestamp header
|
|
201
|
+
timestamp = this.extractTimestamp(request);
|
|
202
|
+
}
|
|
203
|
+
// Validate timestamp if required
|
|
94
204
|
if (timestamp && !this.isTimestampValid(timestamp)) {
|
|
95
205
|
return {
|
|
96
206
|
isValid: false,
|
|
97
207
|
error: "Webhook timestamp expired",
|
|
98
|
-
platform:
|
|
208
|
+
platform: this.platform,
|
|
99
209
|
};
|
|
100
210
|
}
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
platform: "unknown",
|
|
110
|
-
};
|
|
211
|
+
// Format payload according to platform requirements
|
|
212
|
+
const payload = this.formatPayload(rawBody, request);
|
|
213
|
+
// Verify signature based on platform configuration
|
|
214
|
+
let isValid = false;
|
|
215
|
+
const algorithm = this.config.algorithm.replace("hmac-", "");
|
|
216
|
+
if (this.config.customConfig?.encoding === "base64") {
|
|
217
|
+
// For platforms like Clerk that use base64 encoding
|
|
218
|
+
isValid = this.verifyHMACWithBase64(payload, signature, algorithm);
|
|
111
219
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
220
|
+
else if (this.config.headerFormat === "prefixed") {
|
|
221
|
+
// For platforms like GitHub that use prefixed signatures
|
|
222
|
+
isValid = this.verifyHMACWithPrefix(payload, signature, algorithm);
|
|
115
223
|
}
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
224
|
+
else {
|
|
225
|
+
// Standard HMAC verification
|
|
226
|
+
isValid = this.verifyHMAC(payload, signature, algorithm);
|
|
119
227
|
}
|
|
120
|
-
return {
|
|
121
|
-
isValid: true,
|
|
122
|
-
platform: "unknown",
|
|
123
|
-
payload: parsedPayload,
|
|
124
|
-
metadata: {
|
|
125
|
-
timestamp: timestamp?.toString(),
|
|
126
|
-
algorithm: "hmac-sha256",
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
return {
|
|
132
|
-
isValid: false,
|
|
133
|
-
error: `HMAC-SHA256 verification error: ${error.message}`,
|
|
134
|
-
platform: "unknown",
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
exports.HMACSHA256Verifier = HMACSHA256Verifier;
|
|
140
|
-
class HMACSHA1Verifier extends AlgorithmBasedVerifier {
|
|
141
|
-
async verify(request) {
|
|
142
|
-
try {
|
|
143
|
-
const signature = this.extractSignature(request);
|
|
144
|
-
if (!signature) {
|
|
145
|
-
return {
|
|
146
|
-
isValid: false,
|
|
147
|
-
error: `Missing signature header: ${this.config.headerName}`,
|
|
148
|
-
platform: "unknown",
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
const rawBody = await request.text();
|
|
152
|
-
const timestamp = this.extractTimestamp(request);
|
|
153
|
-
if (timestamp && !this.isTimestampValid(timestamp)) {
|
|
154
|
-
return {
|
|
155
|
-
isValid: false,
|
|
156
|
-
error: "Webhook timestamp expired",
|
|
157
|
-
platform: "unknown",
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
const payload = this.formatPayload(rawBody, timestamp);
|
|
161
|
-
const isValid = this.config.prefix
|
|
162
|
-
? this.verifyHMACWithPrefix(payload, signature, "sha1")
|
|
163
|
-
: this.verifyHMAC(payload, signature, "sha1");
|
|
164
228
|
if (!isValid) {
|
|
165
229
|
return {
|
|
166
230
|
isValid: false,
|
|
167
231
|
error: "Invalid signature",
|
|
168
|
-
platform:
|
|
232
|
+
platform: this.platform,
|
|
169
233
|
};
|
|
170
234
|
}
|
|
235
|
+
// Parse payload
|
|
171
236
|
let parsedPayload;
|
|
172
237
|
try {
|
|
173
238
|
parsedPayload = JSON.parse(rawBody);
|
|
@@ -175,93 +240,51 @@ class HMACSHA1Verifier extends AlgorithmBasedVerifier {
|
|
|
175
240
|
catch (e) {
|
|
176
241
|
parsedPayload = rawBody;
|
|
177
242
|
}
|
|
243
|
+
// Extract platform-specific metadata
|
|
244
|
+
const metadata = this.extractMetadata(request);
|
|
178
245
|
return {
|
|
179
246
|
isValid: true,
|
|
180
|
-
platform:
|
|
247
|
+
platform: this.platform,
|
|
181
248
|
payload: parsedPayload,
|
|
182
|
-
metadata
|
|
183
|
-
timestamp: timestamp?.toString(),
|
|
184
|
-
algorithm: "hmac-sha1",
|
|
185
|
-
},
|
|
249
|
+
metadata,
|
|
186
250
|
};
|
|
187
251
|
}
|
|
188
252
|
catch (error) {
|
|
189
253
|
return {
|
|
190
254
|
isValid: false,
|
|
191
|
-
error:
|
|
192
|
-
platform:
|
|
255
|
+
error: `${this.platform} verification error: ${error.message}`,
|
|
256
|
+
platform: this.platform,
|
|
193
257
|
};
|
|
194
258
|
}
|
|
195
259
|
}
|
|
196
260
|
}
|
|
261
|
+
exports.GenericHMACVerifier = GenericHMACVerifier;
|
|
262
|
+
// Legacy verifiers for backward compatibility
|
|
263
|
+
class HMACSHA256Verifier extends GenericHMACVerifier {
|
|
264
|
+
constructor(secret, config, platform = "unknown", toleranceInSeconds = 300) {
|
|
265
|
+
super(secret, config, platform, toleranceInSeconds);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.HMACSHA256Verifier = HMACSHA256Verifier;
|
|
269
|
+
class HMACSHA1Verifier extends GenericHMACVerifier {
|
|
270
|
+
constructor(secret, config, platform = "unknown", toleranceInSeconds = 300) {
|
|
271
|
+
super(secret, config, platform, toleranceInSeconds);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
197
274
|
exports.HMACSHA1Verifier = HMACSHA1Verifier;
|
|
198
|
-
class HMACSHA512Verifier extends
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const signature = this.extractSignature(request);
|
|
202
|
-
if (!signature) {
|
|
203
|
-
return {
|
|
204
|
-
isValid: false,
|
|
205
|
-
error: `Missing signature header: ${this.config.headerName}`,
|
|
206
|
-
platform: "unknown",
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const rawBody = await request.text();
|
|
210
|
-
const timestamp = this.extractTimestamp(request);
|
|
211
|
-
if (timestamp && !this.isTimestampValid(timestamp)) {
|
|
212
|
-
return {
|
|
213
|
-
isValid: false,
|
|
214
|
-
error: "Webhook timestamp expired",
|
|
215
|
-
platform: "unknown",
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
const payload = this.formatPayload(rawBody, timestamp);
|
|
219
|
-
const isValid = this.config.prefix
|
|
220
|
-
? this.verifyHMACWithPrefix(payload, signature, "sha512")
|
|
221
|
-
: this.verifyHMAC(payload, signature, "sha512");
|
|
222
|
-
if (!isValid) {
|
|
223
|
-
return {
|
|
224
|
-
isValid: false,
|
|
225
|
-
error: "Invalid signature",
|
|
226
|
-
platform: "unknown",
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
let parsedPayload;
|
|
230
|
-
try {
|
|
231
|
-
parsedPayload = JSON.parse(rawBody);
|
|
232
|
-
}
|
|
233
|
-
catch (e) {
|
|
234
|
-
parsedPayload = rawBody;
|
|
235
|
-
}
|
|
236
|
-
return {
|
|
237
|
-
isValid: true,
|
|
238
|
-
platform: "unknown",
|
|
239
|
-
payload: parsedPayload,
|
|
240
|
-
metadata: {
|
|
241
|
-
timestamp: timestamp?.toString(),
|
|
242
|
-
algorithm: "hmac-sha512",
|
|
243
|
-
},
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
return {
|
|
248
|
-
isValid: false,
|
|
249
|
-
error: `HMAC-SHA512 verification error: ${error.message}`,
|
|
250
|
-
platform: "unknown",
|
|
251
|
-
};
|
|
252
|
-
}
|
|
275
|
+
class HMACSHA512Verifier extends GenericHMACVerifier {
|
|
276
|
+
constructor(secret, config, platform = "unknown", toleranceInSeconds = 300) {
|
|
277
|
+
super(secret, config, platform, toleranceInSeconds);
|
|
253
278
|
}
|
|
254
279
|
}
|
|
255
280
|
exports.HMACSHA512Verifier = HMACSHA512Verifier;
|
|
256
281
|
// Factory function to create verifiers based on algorithm
|
|
257
|
-
function createAlgorithmVerifier(secret, config, toleranceInSeconds = 300) {
|
|
282
|
+
function createAlgorithmVerifier(secret, config, platform = "unknown", toleranceInSeconds = 300) {
|
|
258
283
|
switch (config.algorithm) {
|
|
259
284
|
case "hmac-sha256":
|
|
260
|
-
return new HMACSHA256Verifier(secret, config, toleranceInSeconds);
|
|
261
285
|
case "hmac-sha1":
|
|
262
|
-
return new HMACSHA1Verifier(secret, config, toleranceInSeconds);
|
|
263
286
|
case "hmac-sha512":
|
|
264
|
-
return new
|
|
287
|
+
return new GenericHMACVerifier(secret, config, platform, toleranceInSeconds);
|
|
265
288
|
case "rsa-sha256":
|
|
266
289
|
case "ed25519":
|
|
267
290
|
case "custom":
|
|
@@ -5,14 +5,4 @@ export declare class TokenBasedVerifier extends WebhookVerifier {
|
|
|
5
5
|
constructor(secret: string, config: SignatureConfig, toleranceInSeconds?: number);
|
|
6
6
|
verify(request: Request): Promise<WebhookVerificationResult>;
|
|
7
7
|
}
|
|
8
|
-
export declare class ClerkCustomVerifier extends WebhookVerifier {
|
|
9
|
-
private config;
|
|
10
|
-
constructor(secret: string, config: SignatureConfig, toleranceInSeconds?: number);
|
|
11
|
-
verify(request: Request): Promise<WebhookVerificationResult>;
|
|
12
|
-
}
|
|
13
|
-
export declare class StripeCustomVerifier extends WebhookVerifier {
|
|
14
|
-
private config;
|
|
15
|
-
constructor(secret: string, config: SignatureConfig, toleranceInSeconds?: number);
|
|
16
|
-
verify(request: Request): Promise<WebhookVerificationResult>;
|
|
17
|
-
}
|
|
18
8
|
export declare function createCustomVerifier(secret: string, config: SignatureConfig, toleranceInSeconds?: number): WebhookVerifier;
|
|
@@ -1,42 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
3
|
+
exports.TokenBasedVerifier = void 0;
|
|
37
4
|
exports.createCustomVerifier = createCustomVerifier;
|
|
38
5
|
const base_1 = require("./base");
|
|
39
|
-
//
|
|
6
|
+
// Token-based verifier for platforms like Supabase
|
|
40
7
|
class TokenBasedVerifier extends base_1.WebhookVerifier {
|
|
41
8
|
constructor(secret, config, toleranceInSeconds = 300) {
|
|
42
9
|
super(secret, toleranceInSeconds);
|
|
@@ -50,7 +17,7 @@ class TokenBasedVerifier extends base_1.WebhookVerifier {
|
|
|
50
17
|
return {
|
|
51
18
|
isValid: false,
|
|
52
19
|
error: `Missing token header: ${this.config.headerName}`,
|
|
53
|
-
platform: "
|
|
20
|
+
platform: "custom",
|
|
54
21
|
};
|
|
55
22
|
}
|
|
56
23
|
// Simple token comparison
|
|
@@ -59,7 +26,7 @@ class TokenBasedVerifier extends base_1.WebhookVerifier {
|
|
|
59
26
|
return {
|
|
60
27
|
isValid: false,
|
|
61
28
|
error: "Invalid token",
|
|
62
|
-
platform: "
|
|
29
|
+
platform: "custom",
|
|
63
30
|
};
|
|
64
31
|
}
|
|
65
32
|
const rawBody = await request.text();
|
|
@@ -72,7 +39,7 @@ class TokenBasedVerifier extends base_1.WebhookVerifier {
|
|
|
72
39
|
}
|
|
73
40
|
return {
|
|
74
41
|
isValid: true,
|
|
75
|
-
platform: "
|
|
42
|
+
platform: "custom",
|
|
76
43
|
payload,
|
|
77
44
|
metadata: {
|
|
78
45
|
id,
|
|
@@ -84,194 +51,18 @@ class TokenBasedVerifier extends base_1.WebhookVerifier {
|
|
|
84
51
|
return {
|
|
85
52
|
isValid: false,
|
|
86
53
|
error: `Token-based verification error: ${error.message}`,
|
|
87
|
-
platform: "
|
|
54
|
+
platform: "custom",
|
|
88
55
|
};
|
|
89
56
|
}
|
|
90
57
|
}
|
|
91
58
|
}
|
|
92
59
|
exports.TokenBasedVerifier = TokenBasedVerifier;
|
|
93
|
-
//
|
|
94
|
-
class ClerkCustomVerifier extends base_1.WebhookVerifier {
|
|
95
|
-
constructor(secret, config, toleranceInSeconds = 300) {
|
|
96
|
-
super(secret, toleranceInSeconds);
|
|
97
|
-
this.config = config;
|
|
98
|
-
}
|
|
99
|
-
async verify(request) {
|
|
100
|
-
try {
|
|
101
|
-
const body = await request.text();
|
|
102
|
-
const svixId = request.headers.get("svix-id");
|
|
103
|
-
const svixTimestamp = request.headers.get("svix-timestamp");
|
|
104
|
-
const svixSignature = request.headers.get("svix-signature");
|
|
105
|
-
if (!svixId || !svixTimestamp || !svixSignature) {
|
|
106
|
-
return {
|
|
107
|
-
isValid: false,
|
|
108
|
-
error: "Missing required Clerk webhook headers",
|
|
109
|
-
platform: "clerk",
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
const timestamp = parseInt(svixTimestamp, 10);
|
|
113
|
-
if (!this.isTimestampValid(timestamp)) {
|
|
114
|
-
return {
|
|
115
|
-
isValid: false,
|
|
116
|
-
error: "Webhook timestamp is too old",
|
|
117
|
-
platform: "clerk",
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const signedContent = `${svixId}.${svixTimestamp}.${body}`;
|
|
121
|
-
const secretBytes = new Uint8Array(Buffer.from(this.secret.split("_")[1], "base64"));
|
|
122
|
-
const { createHmac } = await Promise.resolve().then(() => __importStar(require("crypto")));
|
|
123
|
-
const expectedSignature = createHmac("sha256", secretBytes)
|
|
124
|
-
.update(signedContent)
|
|
125
|
-
.digest("base64");
|
|
126
|
-
const signatures = svixSignature.split(" ");
|
|
127
|
-
let isValid = false;
|
|
128
|
-
for (const sig of signatures) {
|
|
129
|
-
const [version, signature] = sig.split(",");
|
|
130
|
-
if (version === "v1" &&
|
|
131
|
-
this.safeCompare(signature, expectedSignature)) {
|
|
132
|
-
isValid = true;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (!isValid) {
|
|
137
|
-
return {
|
|
138
|
-
isValid: false,
|
|
139
|
-
error: "Invalid signature",
|
|
140
|
-
platform: "clerk",
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
isValid: true,
|
|
145
|
-
platform: "clerk",
|
|
146
|
-
payload: JSON.parse(body),
|
|
147
|
-
metadata: {
|
|
148
|
-
id: svixId,
|
|
149
|
-
timestamp: svixTimestamp,
|
|
150
|
-
algorithm: "clerk-custom",
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
return {
|
|
156
|
-
isValid: false,
|
|
157
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
158
|
-
platform: "clerk",
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
exports.ClerkCustomVerifier = ClerkCustomVerifier;
|
|
164
|
-
// Custom verifier for Stripe's specific format
|
|
165
|
-
class StripeCustomVerifier extends base_1.WebhookVerifier {
|
|
166
|
-
constructor(secret, config, toleranceInSeconds = 300) {
|
|
167
|
-
super(secret, toleranceInSeconds);
|
|
168
|
-
this.config = config;
|
|
169
|
-
}
|
|
170
|
-
async verify(request) {
|
|
171
|
-
try {
|
|
172
|
-
const signature = request.headers.get("Stripe-Signature") ||
|
|
173
|
-
request.headers.get("stripe-signature") ||
|
|
174
|
-
request.headers.get("x-stripe-signature");
|
|
175
|
-
if (!signature) {
|
|
176
|
-
return {
|
|
177
|
-
isValid: false,
|
|
178
|
-
error: "Missing Stripe signature header",
|
|
179
|
-
platform: "stripe",
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
const rawBody = await request.text();
|
|
183
|
-
const sigParts = signature.split(",");
|
|
184
|
-
const sigMap = {};
|
|
185
|
-
for (const part of sigParts) {
|
|
186
|
-
const [key, value] = part.split("=");
|
|
187
|
-
if (key && value) {
|
|
188
|
-
sigMap[key] = value;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const timestamp = sigMap.t;
|
|
192
|
-
const sig = sigMap.v1;
|
|
193
|
-
if (!timestamp || !sig) {
|
|
194
|
-
return {
|
|
195
|
-
isValid: false,
|
|
196
|
-
error: "Invalid Stripe signature format",
|
|
197
|
-
platform: "stripe",
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
const timestampNum = parseInt(timestamp, 10);
|
|
201
|
-
if (!this.isTimestampValid(timestampNum)) {
|
|
202
|
-
return {
|
|
203
|
-
isValid: false,
|
|
204
|
-
error: "Stripe webhook timestamp expired",
|
|
205
|
-
platform: "stripe",
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
const signedPayload = `${timestamp}.${rawBody}`;
|
|
209
|
-
const { createHmac } = await Promise.resolve().then(() => __importStar(require("crypto")));
|
|
210
|
-
const hmac = createHmac("sha256", this.secret);
|
|
211
|
-
hmac.update(signedPayload);
|
|
212
|
-
const expectedSignature = hmac.digest("hex");
|
|
213
|
-
const isValid = this.safeCompare(sig, expectedSignature);
|
|
214
|
-
if (!isValid) {
|
|
215
|
-
console.error("Stripe signature verification failed:", {
|
|
216
|
-
received: sig,
|
|
217
|
-
expected: expectedSignature,
|
|
218
|
-
timestamp,
|
|
219
|
-
bodyLength: rawBody.length,
|
|
220
|
-
signedPayload: `${signedPayload.substring(0, 50)}...`,
|
|
221
|
-
});
|
|
222
|
-
return {
|
|
223
|
-
isValid: false,
|
|
224
|
-
error: "Invalid Stripe signature",
|
|
225
|
-
platform: "stripe",
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
let payload;
|
|
229
|
-
try {
|
|
230
|
-
payload = JSON.parse(rawBody);
|
|
231
|
-
}
|
|
232
|
-
catch (e) {
|
|
233
|
-
return {
|
|
234
|
-
isValid: true,
|
|
235
|
-
platform: "stripe",
|
|
236
|
-
metadata: {
|
|
237
|
-
timestamp,
|
|
238
|
-
id: sigMap.id,
|
|
239
|
-
algorithm: "stripe-custom",
|
|
240
|
-
},
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
return {
|
|
244
|
-
isValid: true,
|
|
245
|
-
platform: "stripe",
|
|
246
|
-
payload,
|
|
247
|
-
metadata: {
|
|
248
|
-
timestamp,
|
|
249
|
-
id: sigMap.id,
|
|
250
|
-
algorithm: "stripe-custom",
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
console.error("Stripe verification error:", error);
|
|
256
|
-
return {
|
|
257
|
-
isValid: false,
|
|
258
|
-
error: `Stripe verification error: ${error.message}`,
|
|
259
|
-
platform: "stripe",
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
exports.StripeCustomVerifier = StripeCustomVerifier;
|
|
265
|
-
// Factory function for custom verifiers
|
|
60
|
+
// Factory function to create custom verifiers
|
|
266
61
|
function createCustomVerifier(secret, config, toleranceInSeconds = 300) {
|
|
267
62
|
const customType = config.customConfig?.type;
|
|
268
63
|
switch (customType) {
|
|
269
64
|
case "token-based":
|
|
270
65
|
return new TokenBasedVerifier(secret, config, toleranceInSeconds);
|
|
271
|
-
case "clerk-custom":
|
|
272
|
-
return new ClerkCustomVerifier(secret, config, toleranceInSeconds);
|
|
273
|
-
case "stripe-custom":
|
|
274
|
-
return new StripeCustomVerifier(secret, config, toleranceInSeconds);
|
|
275
66
|
default:
|
|
276
67
|
// Fallback to token-based for unknown custom types
|
|
277
68
|
return new TokenBasedVerifier(secret, config, toleranceInSeconds);
|