@isnap/sdk 1.4.0-next.204 → 2.0.0-next.205
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/CHANGELOG.md +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/webhooks/verify.d.ts +10 -15
- package/dist/webhooks/verify.d.ts.map +1 -1
- package/dist/webhooks/verify.js +27 -68
- package/dist/webhooks/verify.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,9 @@ All notable changes to `@isnap/sdk` will be documented in this file. The format
|
|
|
28
28
|
- `coverage.test.ts` guard: for every `/v1` operation in the published `openapi.json`, asserts an SDK wrapper exists (or is listed as deliberately un-wrapped, each with a reason). The expected set is derived from the spec, not hand-written, so a newly published endpoint reddens the suite until it is wrapped.
|
|
29
29
|
- `verifyWebhook` verifies the **Standard Webhooks** headers (`webhook-id` / `webhook-timestamp` / `webhook-signature`, WHA-3648) whenever `webhook-signature` is present, and falls back to the `X-iSnap-Signature` / `X-iSnap-Timestamp` scheme otherwise (additive, MINOR — `1.4.0`). Every iSnap delivery now carries both header sets over the same body, so existing callers keep working unchanged; the same secret string (`whsec_…`, or a legacy `whs_…`) and the same `[newSecret, oldSecret]` rotation array work on both paths, and the tolerance applies to both. When the standard header is present it decides: a wrong standard signature is rejected even if the legacy one would pass. The Standard Webhooks key is the base64 decoding of the secret without its prefix, which is what every official Standard Webhooks library derives from the same `whsec_` string.
|
|
30
30
|
|
|
31
|
+
### Removed
|
|
32
|
+
- **`verifyWebhook` verifies the Standard Webhooks headers only** (`webhook-id` / `webhook-timestamp` / `webhook-signature`, WHA-3658) — breaking, MAJOR (`2.0.0`). The fallback to the `X-iSnap-Signature` / `X-iSnap-Timestamp` pair is removed, because iSnap no longer sends those headers: a request that carries only them is now rejected with `WebhookSignatureError` (`Missing webhook-id header`). The key derivation no longer strips a `whs_` prefix either; only `whsec_` secrets, the only kind iSnap mints, verify. Unchanged: the `[newSecret, oldSecret]` rotation array, the `tolerance` option and its 300 s default, case-insensitive header reads, and the return value. A caller that already receives iSnap deliveries needs no code change, since every delivery since `1.4.0` carries the standard headers.
|
|
33
|
+
|
|
31
34
|
### Changed
|
|
32
35
|
- `src/generated/openapi.d.ts` is now generated from the **published** contract. `packages/backend/openapi.json` carries only the partner-facing `/v1/*` surface, so the internal operator routes (`/admin`, `/va`, `/farm-ops`, `/kam`, `/auth`, …) and the bridge-only `/v1/device/*` contract — plus every schema reachable only from them — are no longer emitted into the SDK types. Those routes are the dashboard, the ops back-office and the device bridge; none of them is part of the partner contract this SDK wraps, and no `ISnapClient` method referenced their types, so no documented method changes shape.
|
|
33
36
|
- **`AppDownloadEntry.version` is now `string | null`** (API `1.15.0`, WHA-2538) — source-breaking for a consumer that renders `apps.manifest()`'s `version` without narrowing, which is the point. The field used to be a per-deployment configured string tied to no artifact: staging advertised `0.1.0-staging` for both platforms while the Mac ran `0.0.209` and the update channel published `0.0.x`, so the number could not be installed and the two platforms agreed only because both read one env value. It is now read from the release feed beside the artifact the download URL resolves to, and is `null` when that feed publishes nothing. Render the absence; do not substitute a number, and do not treat `null` as an error. It is also no longer the same string `byod.pair` returns as `bridge_version`.
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// contract surface. Hand-maintained: no build or publish step derives this
|
|
5
5
|
// from `package.json`, so it MUST be bumped in the same commit as the
|
|
6
6
|
// `version` field there. `src/version.test.ts` fails CI when they disagree.
|
|
7
|
-
export const SDK_VERSION = '
|
|
7
|
+
export const SDK_VERSION = '2.0.0';
|
|
8
8
|
// Minimum API version the SDK considers compatible. Reference-only
|
|
9
9
|
// today: the client exposes `client.version()` to read `/v1/version` on
|
|
10
10
|
// demand, but does NOT call it automatically at first request as a
|
|
@@ -15,21 +15,16 @@ export interface VerifyWebhookOptions {
|
|
|
15
15
|
}
|
|
16
16
|
type WebhookHeaders = Record<string, string | string[] | undefined> | Headers;
|
|
17
17
|
/**
|
|
18
|
-
* Verify an incoming webhook delivery
|
|
19
|
-
*
|
|
18
|
+
* Verify an incoming webhook delivery against its **Standard Webhooks**
|
|
19
|
+
* signature (https://www.standardwebhooks.com): `webhook-id`,
|
|
20
|
+
* `webhook-timestamp`, and `webhook-signature` carrying `v1,` + base64
|
|
21
|
+
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`, where
|
|
22
|
+
* the key is the base64 decoding of the secret without its `whsec_`
|
|
23
|
+
* prefix. Pass the secret string exactly as the dashboard shows it.
|
|
20
24
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`,
|
|
25
|
-
* where the key is the base64 decoding of the secret without its
|
|
26
|
-
* `whsec_` (or legacy `whs_`) prefix.
|
|
27
|
-
* - **The iSnap scheme**, the fallback when there is no
|
|
28
|
-
* `webhook-signature`: `X-iSnap-Signature` carries the hex-encoded
|
|
29
|
-
* `HMAC-SHA256(secret, "<timestamp>.<rawBody>")` and `X-iSnap-Timestamp`
|
|
30
|
-
* the unix-second issuance time.
|
|
31
|
-
*
|
|
32
|
-
* Both paths take the secret string exactly as the dashboard shows it.
|
|
25
|
+
* This is the only scheme iSnap signs with. The former `X-iSnap-Signature`
|
|
26
|
+
* / `X-iSnap-Timestamp` pair is no longer sent and no longer accepted: a
|
|
27
|
+
* request that carries only those headers is rejected.
|
|
33
28
|
*
|
|
34
29
|
* Throws `WebhookSignatureError` on missing headers, malformed
|
|
35
30
|
* timestamp, expired timestamp (outside `tolerance`), or signature
|
|
@@ -45,7 +40,7 @@ type WebhookHeaders = Record<string, string | string[] | undefined> | Headers;
|
|
|
45
40
|
*
|
|
46
41
|
* **Consumers MUST dedupe by `event.event_id`** (also sent as `webhook-id`)
|
|
47
42
|
* — retries replay the same envelope (same `event_id`, same body,
|
|
48
|
-
* refreshed timestamp and
|
|
43
|
+
* refreshed timestamp and signature). Without consumer-side dedup, an
|
|
49
44
|
* at-least-once delivery model produces duplicate side-effects on the
|
|
50
45
|
* partner system.
|
|
51
46
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,OAAO,CAAA;AAE7E
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,OAAO,CAAA;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,OAAO,GAAE,oBAAyB,GACjC,gBAAgB,CA6ClB"}
|
package/dist/webhooks/verify.js
CHANGED
|
@@ -3,21 +3,16 @@ import { WebhookSignatureError } from '../errors.js';
|
|
|
3
3
|
import { parseWebhookEvent } from './parse.js';
|
|
4
4
|
const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
5
5
|
/**
|
|
6
|
-
* Verify an incoming webhook delivery
|
|
7
|
-
*
|
|
6
|
+
* Verify an incoming webhook delivery against its **Standard Webhooks**
|
|
7
|
+
* signature (https://www.standardwebhooks.com): `webhook-id`,
|
|
8
|
+
* `webhook-timestamp`, and `webhook-signature` carrying `v1,` + base64
|
|
9
|
+
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`, where
|
|
10
|
+
* the key is the base64 decoding of the secret without its `whsec_`
|
|
11
|
+
* prefix. Pass the secret string exactly as the dashboard shows it.
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<rawBody>")`,
|
|
13
|
-
* where the key is the base64 decoding of the secret without its
|
|
14
|
-
* `whsec_` (or legacy `whs_`) prefix.
|
|
15
|
-
* - **The iSnap scheme**, the fallback when there is no
|
|
16
|
-
* `webhook-signature`: `X-iSnap-Signature` carries the hex-encoded
|
|
17
|
-
* `HMAC-SHA256(secret, "<timestamp>.<rawBody>")` and `X-iSnap-Timestamp`
|
|
18
|
-
* the unix-second issuance time.
|
|
19
|
-
*
|
|
20
|
-
* Both paths take the secret string exactly as the dashboard shows it.
|
|
13
|
+
* This is the only scheme iSnap signs with. The former `X-iSnap-Signature`
|
|
14
|
+
* / `X-iSnap-Timestamp` pair is no longer sent and no longer accepted: a
|
|
15
|
+
* request that carries only those headers is rejected.
|
|
21
16
|
*
|
|
22
17
|
* Throws `WebhookSignatureError` on missing headers, malformed
|
|
23
18
|
* timestamp, expired timestamp (outside `tolerance`), or signature
|
|
@@ -33,48 +28,11 @@ const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
|
33
28
|
*
|
|
34
29
|
* **Consumers MUST dedupe by `event.event_id`** (also sent as `webhook-id`)
|
|
35
30
|
* — retries replay the same envelope (same `event_id`, same body,
|
|
36
|
-
* refreshed timestamp and
|
|
31
|
+
* refreshed timestamp and signature). Without consumer-side dedup, an
|
|
37
32
|
* at-least-once delivery model produces duplicate side-effects on the
|
|
38
33
|
* partner system.
|
|
39
34
|
*/
|
|
40
35
|
export function verifyWebhook(rawBody, headers, secret, options = {}) {
|
|
41
|
-
if (getHeader(headers, 'webhook-signature') !== null) {
|
|
42
|
-
return verifyStandardWebhook(rawBody, headers, secret, options);
|
|
43
|
-
}
|
|
44
|
-
return verifyISnapWebhook(rawBody, headers, secret, options);
|
|
45
|
-
}
|
|
46
|
-
function verifyISnapWebhook(rawBody, headers, secret, options) {
|
|
47
|
-
const sigHeader = getHeader(headers, 'x-isnap-signature');
|
|
48
|
-
if (!sigHeader) {
|
|
49
|
-
throw new WebhookSignatureError('Missing X-iSnap-Signature header');
|
|
50
|
-
}
|
|
51
|
-
const tsHeader = getHeader(headers, 'x-isnap-timestamp');
|
|
52
|
-
if (!tsHeader) {
|
|
53
|
-
throw new WebhookSignatureError('Missing X-iSnap-Timestamp header');
|
|
54
|
-
}
|
|
55
|
-
const timestamp = checkTimestamp(tsHeader, 'X-iSnap-Timestamp', options);
|
|
56
|
-
const candidates = secretCandidates(secret);
|
|
57
|
-
const provided = decodeHex(sigHeader);
|
|
58
|
-
if (!provided) {
|
|
59
|
-
throw new WebhookSignatureError('X-iSnap-Signature is not valid hex');
|
|
60
|
-
}
|
|
61
|
-
const signedPayload = `${timestamp}.${rawBody}`;
|
|
62
|
-
// Multi-secret loop returns early on first match — this is a minor
|
|
63
|
-
// timing side-channel during the rotate-secret grace window (an
|
|
64
|
-
// attacker who controls request timing could distinguish "matched first
|
|
65
|
-
// candidate" from "matched second"). Practical exploit value is near
|
|
66
|
-
// zero: knowing which secret matched does not help forge signatures
|
|
67
|
-
// for either secret. Worth revisiting if real partners run rotation
|
|
68
|
-
// through hostile networks.
|
|
69
|
-
for (const candidate of candidates) {
|
|
70
|
-
const expected = createHmac('sha256', candidate).update(signedPayload).digest();
|
|
71
|
-
if (provided.length === expected.length && timingSafeEqual(provided, expected)) {
|
|
72
|
-
return parseWebhookEvent(rawBody);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
throw new WebhookSignatureError('Webhook signature did not match any provided secret');
|
|
76
|
-
}
|
|
77
|
-
function verifyStandardWebhook(rawBody, headers, secret, options) {
|
|
78
36
|
const msgId = getHeader(headers, 'webhook-id');
|
|
79
37
|
if (!msgId) {
|
|
80
38
|
throw new WebhookSignatureError('Missing webhook-id header');
|
|
@@ -83,12 +41,16 @@ function verifyStandardWebhook(rawBody, headers, secret, options) {
|
|
|
83
41
|
if (!tsHeader) {
|
|
84
42
|
throw new WebhookSignatureError('Missing webhook-timestamp header');
|
|
85
43
|
}
|
|
44
|
+
const sigHeader = getHeader(headers, 'webhook-signature');
|
|
45
|
+
if (!sigHeader) {
|
|
46
|
+
throw new WebhookSignatureError('Missing webhook-signature header');
|
|
47
|
+
}
|
|
86
48
|
const timestamp = checkTimestamp(tsHeader, 'webhook-timestamp', options);
|
|
87
49
|
const candidates = secretCandidates(secret);
|
|
88
50
|
// Space-delimited `<version>,<base64>` pairs; only `v1` (HMAC-SHA256)
|
|
89
51
|
// exists today, and a version this SDK does not know is skipped rather
|
|
90
52
|
// than rejected, so a sender adding a scheme does not break verification.
|
|
91
|
-
const provided =
|
|
53
|
+
const provided = sigHeader
|
|
92
54
|
.split(' ')
|
|
93
55
|
.filter((entry) => entry.startsWith('v1,'))
|
|
94
56
|
.map((entry) => Buffer.from(entry.slice(3), 'base64'));
|
|
@@ -96,7 +58,13 @@ function verifyStandardWebhook(rawBody, headers, secret, options) {
|
|
|
96
58
|
throw new WebhookSignatureError('webhook-signature carries no v1 signature');
|
|
97
59
|
}
|
|
98
60
|
const signedPayload = `${msgId}.${timestamp}.${rawBody}`;
|
|
99
|
-
//
|
|
61
|
+
// Multi-secret loop returns early on first match — this is a minor
|
|
62
|
+
// timing side-channel during the rotate-secret grace window (an
|
|
63
|
+
// attacker who controls request timing could distinguish "matched first
|
|
64
|
+
// candidate" from "matched second"). Practical exploit value is near
|
|
65
|
+
// zero: knowing which secret matched does not help forge signatures
|
|
66
|
+
// for either secret. Worth revisiting if real partners run rotation
|
|
67
|
+
// through hostile networks.
|
|
100
68
|
for (const candidate of candidates) {
|
|
101
69
|
const expected = createHmac('sha256', standardWebhookKey(candidate))
|
|
102
70
|
.update(signedPayload)
|
|
@@ -110,16 +78,12 @@ function verifyStandardWebhook(rawBody, headers, secret, options) {
|
|
|
110
78
|
throw new WebhookSignatureError('Webhook signature did not match any provided secret');
|
|
111
79
|
}
|
|
112
80
|
// The Standard Webhooks key: the base64 decoding of the secret without its
|
|
113
|
-
// prefix
|
|
114
|
-
// `
|
|
115
|
-
// works as-is here and
|
|
116
|
-
// `whs_` secret to those as `whsec_` + the same hex).
|
|
81
|
+
// `whsec_` prefix — what every official Standard Webhooks library derives
|
|
82
|
+
// from the same string. iSnap secrets are `whsec_` followed by hex, which is
|
|
83
|
+
// valid base64, so the dashboard string works as-is here and there.
|
|
117
84
|
function standardWebhookKey(secret) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return Buffer.from(secret.slice(prefix.length), 'base64');
|
|
121
|
-
}
|
|
122
|
-
return Buffer.from(secret, 'base64');
|
|
85
|
+
const encoded = secret.startsWith('whsec_') ? secret.slice('whsec_'.length) : secret;
|
|
86
|
+
return Buffer.from(encoded, 'base64');
|
|
123
87
|
}
|
|
124
88
|
function checkTimestamp(tsHeader, headerName, options) {
|
|
125
89
|
// Strict integer-only check — `Number.parseInt` would happily accept
|
|
@@ -171,11 +135,6 @@ function getHeader(headers, name) {
|
|
|
171
135
|
}
|
|
172
136
|
return null;
|
|
173
137
|
}
|
|
174
|
-
function decodeHex(hex) {
|
|
175
|
-
if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length % 2 !== 0)
|
|
176
|
-
return null;
|
|
177
|
-
return Buffer.from(hex, 'hex');
|
|
178
|
-
}
|
|
179
138
|
function defaultNow() {
|
|
180
139
|
return Math.floor(Date.now() / 1000);
|
|
181
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAmBrC
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/webhooks/verify.ts"],"names":[],"mappings":"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"}
|