@kirimdev/sdk 2.0.1 → 3.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/README.md +39 -5
- package/dist/errors.d.ts +12 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -1
- package/dist/errors.js.map +1 -1
- package/dist/webhooks.d.ts +84 -83
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +102 -41
- package/dist/webhooks.js.map +1 -1
- package/openapi.json +6920 -6920
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,24 +17,58 @@ import { Kirim } from '@kirimdev/sdk'
|
|
|
17
17
|
|
|
18
18
|
const kirim = new Kirim({ apiKey: process.env.KIRIM_API_KEY! })
|
|
19
19
|
|
|
20
|
+
// Scope to a WhatsApp phone number (Meta `business_phone_number_id`).
|
|
21
|
+
// Look it up via `kirim.accounts.list()` if you don't have it handy.
|
|
22
|
+
const phone = kirim.phoneNumbers('106540352242922')
|
|
23
|
+
|
|
20
24
|
// Send a message
|
|
21
|
-
const msg = await
|
|
25
|
+
const msg = await phone.messages.send({
|
|
22
26
|
to: '628123456789',
|
|
23
27
|
type: 'text',
|
|
24
28
|
text: { body: 'Halo dari SDK!' },
|
|
25
29
|
})
|
|
26
30
|
|
|
27
31
|
// Paginate
|
|
28
|
-
for await (const m of
|
|
32
|
+
for await (const m of phone.messages.list({ limit: 50 })) {
|
|
29
33
|
console.log(m.id, m.status)
|
|
30
34
|
}
|
|
35
|
+
```
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
import { verifyWebhookSignature } from '@kirimdev/sdk/webhooks'
|
|
37
|
+
## Webhook verification
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
`verifyWebhookSignature` parses the `X-Kirim-Signature` header (Stripe-style
|
|
40
|
+
`t=<unix>,v1=<hex>` format), checks the timestamp against a tolerance window,
|
|
41
|
+
verifies the HMAC-SHA256 against one or more active secrets (supports rotation),
|
|
42
|
+
and returns the parsed JSON body. It **throws** on every failure mode — never
|
|
43
|
+
returns `false`. Uses Web Crypto so it runs unchanged on Node 18+, Bun, Deno,
|
|
44
|
+
and edge runtimes.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { verifyWebhookSignature, InvalidSignatureError } from '@kirimdev/sdk/webhooks'
|
|
48
|
+
|
|
49
|
+
export async function POST(req: Request) {
|
|
50
|
+
const rawBody = await req.text()
|
|
51
|
+
try {
|
|
52
|
+
const event = await verifyWebhookSignature({
|
|
53
|
+
rawBody,
|
|
54
|
+
signatureHeader: req.headers.get('x-kirim-signature'),
|
|
55
|
+
secrets: [process.env.KIRIM_WEBHOOK_SECRET!],
|
|
56
|
+
})
|
|
57
|
+
// event is KirimWebhookEvent — narrow on event.type or event.object
|
|
58
|
+
return new Response('ok')
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err instanceof InvalidSignatureError) return new Response('bad sig', { status: 401 })
|
|
61
|
+
throw err
|
|
62
|
+
}
|
|
63
|
+
}
|
|
36
64
|
```
|
|
37
65
|
|
|
66
|
+
Errors thrown (all extend `KirimWebhookError`):
|
|
67
|
+
|
|
68
|
+
- `InvalidSignatureError` — header missing/malformed, or no `v1=` matched any provided secret
|
|
69
|
+
- `SignatureExpiredError` — `|now - t|` exceeds `toleranceSeconds` (default 300s)
|
|
70
|
+
- `MalformedPayloadError` — signature matched but body is not valid JSON
|
|
71
|
+
|
|
38
72
|
## Features
|
|
39
73
|
|
|
40
74
|
- Full coverage of the Kirim `/v1` API (~35 endpoints)
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* HTTP error class hierarchy for `@kirimdev/sdk`.
|
|
3
3
|
*
|
|
4
4
|
* Every HTTP error from the Kirim API is mapped to a subclass of
|
|
5
5
|
* `KirimError`. Network/timeout failures throw `ConnectionError`. The
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
* specific stable identifier.
|
|
9
9
|
*
|
|
10
10
|
* Source of truth for codes: `apps/api/src/lib/api-error.ts` (ERROR_CATALOG).
|
|
11
|
+
*
|
|
12
|
+
* NOTE: Webhook signature verification errors live in a SEPARATE hierarchy
|
|
13
|
+
* rooted at `KirimWebhookError` (see `@kirimdev/sdk/webhooks`). They do
|
|
14
|
+
* not extend `KirimError` because they carry no HTTP shape — no
|
|
15
|
+
* `requestId`, `status`, or `type`. Consumers wanting to catch both must
|
|
16
|
+
* catch each base class separately, or use a union check:
|
|
17
|
+
*
|
|
18
|
+
* catch (e) {
|
|
19
|
+
* if (e instanceof KirimError) {...}
|
|
20
|
+
* else if (e instanceof KirimWebhookError) {...}
|
|
21
|
+
* }
|
|
11
22
|
*/
|
|
12
23
|
export type KirimErrorType = 'invalid_request_error' | 'authentication_error' | 'permission_error' | 'not_found' | 'conflict' | 'rate_limit_error' | 'api_error' | 'connection_error';
|
|
13
24
|
export interface KirimErrorPayload {
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,cAAc,GACtB,uBAAuB,GACvB,sBAAsB,GACtB,kBAAkB,GAClB,WAAW,GACX,UAAU,GACV,kBAAkB,GAClB,WAAW,GACX,kBAAkB,CAAA;AAEtB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,6CAA6C;IAC7C,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAED,4DAA4D;AAC5D,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAA;gBAET,OAAO,EAAE,iBAAiB;CAUvC;AAED,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,EAAE,iBAAiB;CAIvC;AAED,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,EAAE,iBAAiB;CAIvC;AAED,qBAAa,eAAgB,SAAQ,UAAU;gBACjC,OAAO,EAAE,iBAAiB;CAIvC;AAED,qBAAa,aAAc,SAAQ,UAAU;gBAC/B,OAAO,EAAE,iBAAiB;CAIvC;AAED,qBAAa,aAAc,SAAQ,UAAU;gBAC/B,OAAO,EAAE,iBAAiB;CAIvC;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC5C,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;gBACtB,OAAO,EAAE,iBAAiB,GAAG;QAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAKvE;AAED,qBAAa,cAAe,SAAQ,UAAU;gBAChC,OAAO,EAAE,iBAAiB;CAIvC;AAED,6DAA6D;AAC7D,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;gBACX,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAW5C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC/B,UAAU,CAiCZ"}
|
package/dist/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* HTTP error class hierarchy for `@kirimdev/sdk`.
|
|
3
3
|
*
|
|
4
4
|
* Every HTTP error from the Kirim API is mapped to a subclass of
|
|
5
5
|
* `KirimError`. Network/timeout failures throw `ConnectionError`. The
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
* specific stable identifier.
|
|
9
9
|
*
|
|
10
10
|
* Source of truth for codes: `apps/api/src/lib/api-error.ts` (ERROR_CATALOG).
|
|
11
|
+
*
|
|
12
|
+
* NOTE: Webhook signature verification errors live in a SEPARATE hierarchy
|
|
13
|
+
* rooted at `KirimWebhookError` (see `@kirimdev/sdk/webhooks`). They do
|
|
14
|
+
* not extend `KirimError` because they carry no HTTP shape — no
|
|
15
|
+
* `requestId`, `status`, or `type`. Consumers wanting to catch both must
|
|
16
|
+
* catch each base class separately, or use a union check:
|
|
17
|
+
*
|
|
18
|
+
* catch (e) {
|
|
19
|
+
* if (e instanceof KirimError) {...}
|
|
20
|
+
* else if (e instanceof KirimWebhookError) {...}
|
|
21
|
+
* }
|
|
11
22
|
*/
|
|
12
23
|
/** Base class. All errors thrown by the SDK extend this. */
|
|
13
24
|
export class KirimError extends Error {
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/webhooks.d.ts
CHANGED
|
@@ -1,100 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Webhook HMAC verification + typed event payloads.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* header. This module exposes a constant-time verifier using Web Crypto
|
|
7
|
-
* (`crypto.subtle`) so it runs on Node 18+, Bun, Deno, and edge runtimes.
|
|
4
|
+
* Kirim signs outbound webhook deliveries using a Stripe-style header
|
|
5
|
+
* format mirroring `apps/worker/src/jobs/webhook-delivery/sign.ts`:
|
|
8
6
|
*
|
|
9
|
-
*
|
|
7
|
+
* X-Kirim-Signature: t=<unix_seconds>,v1=<hex>[,v1=<hex>...]
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* - `t` is the time the signature was computed; callers reject when
|
|
10
|
+
* |now - t| exceeds `toleranceSeconds` (default 5 minutes).
|
|
11
|
+
* - Each `v1=` is HMAC-SHA256 of `${t}.${rawBody}` under one ACTIVE
|
|
12
|
+
* signing secret. During rotation a subscription may have multiple
|
|
13
|
+
* active secrets and the header carries one `v1=` per secret —
|
|
14
|
+
* verification passes if ANY caller-supplied secret matches ANY
|
|
15
|
+
* `v1=` value.
|
|
13
16
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* req.headers.get('x-kirim-signature') ?? '',
|
|
17
|
-
* process.env.KIRIM_WEBHOOK_SECRET!,
|
|
18
|
-
* )
|
|
19
|
-
* if (!ok) return new Response('bad signature', { status: 401 })
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Verify a Kirim webhook signature.
|
|
23
|
-
*
|
|
24
|
-
* MUST be called with the raw request body (the same bytes Kirim signed —
|
|
25
|
-
* re-serializing JSON will break the signature). Returns `false` (never
|
|
26
|
-
* throws) when the header is absent / malformed / mismatched. The caller
|
|
27
|
-
* MUST reject the request when this returns `false` (fail-closed).
|
|
28
|
-
*/
|
|
29
|
-
export declare function verifyWebhookSignature(body: string, signatureHeader: string | null | undefined, secret: string): Promise<boolean>;
|
|
30
|
-
/**
|
|
31
|
-
* Discriminated union of all event types Kirim can deliver to a webhook
|
|
32
|
-
* subscription. The `type` literal is the discriminator.
|
|
17
|
+
* Uses Web Crypto (`crypto.subtle`) so this module runs unchanged on
|
|
18
|
+
* Node 18+, Bun, Deno, and edge runtimes.
|
|
33
19
|
*
|
|
34
|
-
* The
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* `
|
|
20
|
+
* The error classes in this module (`KirimWebhookError`,
|
|
21
|
+
* `InvalidSignatureError`, `SignatureExpiredError`,
|
|
22
|
+
* `MalformedPayloadError`) form a SEPARATE hierarchy from the HTTP
|
|
23
|
+
* `KirimError` tree in `@kirimdev/sdk` — webhook errors carry no
|
|
24
|
+
* `requestId` / `status` / `code` because they fire before any HTTP
|
|
25
|
+
* round-trip. Consumers wanting a single catch surface should branch
|
|
26
|
+
* on `instanceof KirimWebhookError` separately from `instanceof
|
|
27
|
+
* KirimError`.
|
|
38
28
|
*/
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
id: string;
|
|
42
|
-
created_at: string;
|
|
43
|
-
/** Subscription id that triggered the delivery. */
|
|
44
|
-
subscription_id: string;
|
|
29
|
+
export declare class KirimWebhookError extends Error {
|
|
30
|
+
constructor(message: string);
|
|
45
31
|
}
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
data: {
|
|
49
|
-
id: string;
|
|
50
|
-
object: 'message';
|
|
51
|
-
conversation_id?: string;
|
|
52
|
-
to: string;
|
|
53
|
-
type: string;
|
|
54
|
-
status: string;
|
|
55
|
-
created_at: string;
|
|
56
|
-
};
|
|
32
|
+
export declare class InvalidSignatureError extends KirimWebhookError {
|
|
33
|
+
constructor(message?: string);
|
|
57
34
|
}
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
data: {
|
|
61
|
-
id: string;
|
|
62
|
-
object: 'message';
|
|
63
|
-
status: 'sent' | 'delivered' | 'read' | 'failed' | 'queued' | 'pending';
|
|
64
|
-
error?: {
|
|
65
|
-
code: string;
|
|
66
|
-
message: string;
|
|
67
|
-
provider_code: number | null;
|
|
68
|
-
};
|
|
69
|
-
};
|
|
35
|
+
export declare class SignatureExpiredError extends KirimWebhookError {
|
|
36
|
+
constructor(message?: string);
|
|
70
37
|
}
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
data: {
|
|
74
|
-
id: string;
|
|
75
|
-
object: 'conversation';
|
|
76
|
-
};
|
|
38
|
+
export declare class MalformedPayloadError extends KirimWebhookError {
|
|
39
|
+
constructor(message?: string);
|
|
77
40
|
}
|
|
78
|
-
export interface
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
41
|
+
export interface VerifyOptions {
|
|
42
|
+
rawBody: string;
|
|
43
|
+
signatureHeader: string | null | undefined;
|
|
44
|
+
secrets: string[];
|
|
45
|
+
/** Default 300 seconds (5 minutes). */
|
|
46
|
+
toleranceSeconds?: number;
|
|
47
|
+
/** Override the wall clock — primarily for tests. */
|
|
48
|
+
nowSeconds?: () => number;
|
|
84
49
|
}
|
|
85
|
-
export
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
50
|
+
export declare function verifyWebhookSignature(opts: VerifyOptions): Promise<unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Native Kirim event envelope (server emits `conversation.*` and
|
|
53
|
+
* `contact.*` in this shape). The `message.received` and `message.status`
|
|
54
|
+
* events are forwarded VERBATIM from Meta's webhook body and do NOT use
|
|
55
|
+
* this envelope — see `MetaPassthroughBody` below.
|
|
56
|
+
*/
|
|
57
|
+
export interface KirimEventEnvelope<TType extends string, TData> {
|
|
58
|
+
id: string;
|
|
59
|
+
type: TType;
|
|
60
|
+
created_at: string;
|
|
61
|
+
data: TData;
|
|
91
62
|
}
|
|
92
|
-
export
|
|
93
|
-
|
|
94
|
-
|
|
63
|
+
export type ConversationAssignedEvent = KirimEventEnvelope<'conversation.assigned', {
|
|
64
|
+
id: string;
|
|
65
|
+
object: 'conversation';
|
|
66
|
+
[k: string]: unknown;
|
|
67
|
+
}>;
|
|
68
|
+
export type ConversationClosedEvent = KirimEventEnvelope<'conversation.closed', {
|
|
69
|
+
id: string;
|
|
70
|
+
object: 'conversation';
|
|
71
|
+
[k: string]: unknown;
|
|
72
|
+
}>;
|
|
73
|
+
export type ContactCreatedEvent = KirimEventEnvelope<'contact.created', {
|
|
74
|
+
id: string;
|
|
75
|
+
object: 'contact';
|
|
76
|
+
[k: string]: unknown;
|
|
77
|
+
}>;
|
|
78
|
+
export type ContactUpdatedEvent = KirimEventEnvelope<'contact.updated', {
|
|
79
|
+
id: string;
|
|
80
|
+
object: 'contact';
|
|
81
|
+
[k: string]: unknown;
|
|
82
|
+
}>;
|
|
83
|
+
export type KirimNativeWebhookEvent = ConversationAssignedEvent | ConversationClosedEvent | ContactCreatedEvent | ContactUpdatedEvent;
|
|
84
|
+
/**
|
|
85
|
+
* Meta passthrough body — `message.received` and `message.status`
|
|
86
|
+
* deliveries forward Meta's WhatsApp Cloud webhook payload unchanged.
|
|
87
|
+
* Callers should branch on `X-Kirim-Event` to decide whether to parse
|
|
88
|
+
* the body as `KirimNativeWebhookEvent` or `MetaPassthroughBody`.
|
|
89
|
+
*/
|
|
90
|
+
export interface MetaPassthroughBody {
|
|
91
|
+
object: 'whatsapp_business_account';
|
|
92
|
+
entry: Array<{
|
|
95
93
|
id: string;
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
changes: Array<{
|
|
95
|
+
value: unknown;
|
|
96
|
+
field: 'messages' | string;
|
|
97
|
+
}>;
|
|
98
|
+
}>;
|
|
98
99
|
}
|
|
99
|
-
export
|
|
100
|
+
export type KirimWebhookEvent = KirimNativeWebhookEvent | MetaPassthroughBody;
|
|
100
101
|
//# sourceMappingURL=webhooks.d.ts.map
|
package/dist/webhooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,qBAAsB,SAAQ,iBAAiB;gBAC9C,OAAO,SAA8B;CAIlD;AAED,qBAAa,qBAAsB,SAAQ,iBAAiB;gBAC9C,OAAO,SAAyD;CAI7E;AAED,qBAAa,qBAAsB,SAAQ,iBAAiB;gBAC9C,OAAO,SAAsC;CAI1D;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAC1C,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,MAAM,CAAA;CAC1B;AA8BD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBlF;AAgCD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB,CAAC,KAAK,SAAS,MAAM,EAAE,KAAK;IAC7D,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,KAAK,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,KAAK,CAAA;CACZ;AAED,MAAM,MAAM,yBAAyB,GAAG,kBAAkB,CACxD,uBAAuB,EACvB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAC7D,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,CACtD,qBAAqB,EACrB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAC7D,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAClD,iBAAiB,EACjB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CACxD,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAClD,iBAAiB,EACjB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CACxD,CAAA;AAED,MAAM,MAAM,uBAAuB,GAC/B,yBAAyB,GACzB,uBAAuB,GACvB,mBAAmB,GACnB,mBAAmB,CAAA;AAEvB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,2BAA2B,CAAA;IACnC,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAAA;SAAE,CAAC,CAAA;KAC/D,CAAC,CAAA;CACH;AAED,MAAM,MAAM,iBAAiB,GAAG,uBAAuB,GAAG,mBAAmB,CAAA"}
|
package/dist/webhooks.js
CHANGED
|
@@ -1,64 +1,125 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Webhook HMAC verification + typed event payloads.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* header. This module exposes a constant-time verifier using Web Crypto
|
|
7
|
-
* (`crypto.subtle`) so it runs on Node 18+, Bun, Deno, and edge runtimes.
|
|
4
|
+
* Kirim signs outbound webhook deliveries using a Stripe-style header
|
|
5
|
+
* format mirroring `apps/worker/src/jobs/webhook-delivery/sign.ts`:
|
|
8
6
|
*
|
|
9
|
-
*
|
|
7
|
+
* X-Kirim-Signature: t=<unix_seconds>,v1=<hex>[,v1=<hex>...]
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* - `t` is the time the signature was computed; callers reject when
|
|
10
|
+
* |now - t| exceeds `toleranceSeconds` (default 5 minutes).
|
|
11
|
+
* - Each `v1=` is HMAC-SHA256 of `${t}.${rawBody}` under one ACTIVE
|
|
12
|
+
* signing secret. During rotation a subscription may have multiple
|
|
13
|
+
* active secrets and the header carries one `v1=` per secret —
|
|
14
|
+
* verification passes if ANY caller-supplied secret matches ANY
|
|
15
|
+
* `v1=` value.
|
|
13
16
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* req.headers.get('x-kirim-signature') ?? '',
|
|
17
|
-
* process.env.KIRIM_WEBHOOK_SECRET!,
|
|
18
|
-
* )
|
|
19
|
-
* if (!ok) return new Response('bad signature', { status: 401 })
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Verify a Kirim webhook signature.
|
|
17
|
+
* Uses Web Crypto (`crypto.subtle`) so this module runs unchanged on
|
|
18
|
+
* Node 18+, Bun, Deno, and edge runtimes.
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
20
|
+
* The error classes in this module (`KirimWebhookError`,
|
|
21
|
+
* `InvalidSignatureError`, `SignatureExpiredError`,
|
|
22
|
+
* `MalformedPayloadError`) form a SEPARATE hierarchy from the HTTP
|
|
23
|
+
* `KirimError` tree in `@kirimdev/sdk` — webhook errors carry no
|
|
24
|
+
* `requestId` / `status` / `code` because they fire before any HTTP
|
|
25
|
+
* round-trip. Consumers wanting a single catch surface should branch
|
|
26
|
+
* on `instanceof KirimWebhookError` separately from `instanceof
|
|
27
|
+
* KirimError`.
|
|
28
28
|
*/
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
export class KirimWebhookError extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = 'KirimWebhookError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class InvalidSignatureError extends KirimWebhookError {
|
|
36
|
+
constructor(message = 'Invalid webhook signature') {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'InvalidSignatureError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class SignatureExpiredError extends KirimWebhookError {
|
|
42
|
+
constructor(message = 'Webhook signature timestamp outside tolerance window') {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = 'SignatureExpiredError';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class MalformedPayloadError extends KirimWebhookError {
|
|
48
|
+
constructor(message = 'Webhook payload is not valid JSON') {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'MalformedPayloadError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function parseHeader(header) {
|
|
54
|
+
const parts = header.split(',').map((s) => s.trim()).filter(Boolean);
|
|
55
|
+
let t = null;
|
|
56
|
+
const v1 = [];
|
|
57
|
+
for (const part of parts) {
|
|
58
|
+
const eq = part.indexOf('=');
|
|
59
|
+
if (eq <= 0)
|
|
60
|
+
return null;
|
|
61
|
+
const k = part.slice(0, eq);
|
|
62
|
+
const v = part.slice(eq + 1);
|
|
63
|
+
if (k === 't') {
|
|
64
|
+
const n = Number(v);
|
|
65
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
66
|
+
return null;
|
|
67
|
+
t = n;
|
|
68
|
+
}
|
|
69
|
+
else if (k === 'v1') {
|
|
70
|
+
if (!/^[0-9a-f]+$/i.test(v))
|
|
71
|
+
return null;
|
|
72
|
+
v1.push(v.toLowerCase());
|
|
73
|
+
}
|
|
74
|
+
// Unknown scheme tokens (future-proof: e.g. v2=) are ignored.
|
|
75
|
+
}
|
|
76
|
+
if (t === null || v1.length === 0)
|
|
77
|
+
return null;
|
|
78
|
+
return { t, v1 };
|
|
79
|
+
}
|
|
80
|
+
export async function verifyWebhookSignature(opts) {
|
|
81
|
+
const tolerance = opts.toleranceSeconds ?? 300;
|
|
82
|
+
const now = (opts.nowSeconds ?? (() => Math.floor(Date.now() / 1000)))();
|
|
83
|
+
if (!opts.signatureHeader || opts.secrets.length === 0) {
|
|
84
|
+
throw new InvalidSignatureError('Missing signature header or secrets');
|
|
85
|
+
}
|
|
86
|
+
const parsed = parseHeader(opts.signatureHeader);
|
|
87
|
+
if (!parsed)
|
|
88
|
+
throw new InvalidSignatureError('Malformed signature header');
|
|
89
|
+
if (Math.abs(now - parsed.t) > tolerance) {
|
|
90
|
+
throw new SignatureExpiredError();
|
|
91
|
+
}
|
|
92
|
+
const signedString = `${parsed.t}.${opts.rawBody}`;
|
|
93
|
+
for (const secret of opts.secrets) {
|
|
94
|
+
const expected = await hmacSha256Hex(secret, signedString);
|
|
95
|
+
for (const candidate of parsed.v1) {
|
|
96
|
+
if (expected.length === candidate.length && timingSafeEqualHex(expected, candidate)) {
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(opts.rawBody);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new MalformedPayloadError();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw new InvalidSignatureError('No v1 signature matched any provided secret');
|
|
42
107
|
}
|
|
43
108
|
async function hmacSha256Hex(secret, body) {
|
|
44
109
|
const enc = new TextEncoder();
|
|
45
110
|
const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
46
111
|
const buf = await crypto.subtle.sign('HMAC', key, enc.encode(body));
|
|
47
|
-
return bufferToHex(new Uint8Array(buf));
|
|
48
|
-
}
|
|
49
|
-
function bufferToHex(buf) {
|
|
50
112
|
let out = '';
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
out +=
|
|
113
|
+
const view = new Uint8Array(buf);
|
|
114
|
+
for (let i = 0; i < view.length; i++) {
|
|
115
|
+
out += (view[i] ?? 0).toString(16).padStart(2, '0');
|
|
54
116
|
}
|
|
55
117
|
return out;
|
|
56
118
|
}
|
|
57
|
-
/** Constant-time string comparison. Inputs MUST be equal length. */
|
|
58
119
|
function timingSafeEqualHex(a, b) {
|
|
59
120
|
let diff = 0;
|
|
60
121
|
for (let i = 0; i < a.length; i++) {
|
|
61
|
-
diff |=
|
|
122
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
62
123
|
}
|
|
63
124
|
return diff === 0;
|
|
64
125
|
}
|
package/dist/webhooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"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"}
|