@tanqory/plugin-sdk 0.2.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 +117 -0
- package/dist/auth.d.ts +38 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +95 -0
- package/dist/auth.js.map +1 -0
- package/dist/client/index.d.ts +67 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +157 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client.d.ts +53 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +131 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +44 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +54 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +117 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.spec.d.ts +2 -0
- package/dist/manifest.spec.d.ts.map +1 -0
- package/dist/manifest.spec.js +82 -0
- package/dist/manifest.spec.js.map +1 -0
- package/dist/webhooks.d.ts +22 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +74 -0
- package/dist/webhooks.js.map +1 -0
- package/dist/webhooks.spec.d.ts +2 -0
- package/dist/webhooks.spec.d.ts.map +1 -0
- package/dist/webhooks.spec.js +74 -0
- package/dist/webhooks.spec.js.map +1 -0
- package/package.json +67 -0
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/**
|
|
6
|
+
* How long a signed payload stays acceptable.
|
|
7
|
+
*
|
|
8
|
+
* Without this, a signature is valid forever: anyone who captures one delivery
|
|
9
|
+
* can replay it at any point in the future and it still verifies. Five minutes
|
|
10
|
+
* is enough slack for clock drift and a slow queue, and short enough that a
|
|
11
|
+
* captured request is not a permanent liability.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
14
|
+
/**
|
|
15
|
+
* Verify that a webhook really came from Tanqory and has not been replayed.
|
|
16
|
+
*
|
|
17
|
+
* Pass the **raw** request body — the exact bytes received. Re-serialising a
|
|
18
|
+
* parsed object changes key order and whitespace, and the signature will not
|
|
19
|
+
* match. In frameworks that parse JSON for you, read the raw body first.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* const raw = await request.text()
|
|
23
|
+
* if (!verifyWebhookSignature(raw, request.headers.get('x-tanqory-signature'), secret)) {
|
|
24
|
+
* return new Response('bad signature', { status: 401 })
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function verifyWebhookSignature(rawBody, signatureHeader, secret, options = {}) {
|
|
29
|
+
if (!signatureHeader || !secret)
|
|
30
|
+
return false;
|
|
31
|
+
const parsed = parseSignatureHeader(signatureHeader);
|
|
32
|
+
if (!parsed)
|
|
33
|
+
return false;
|
|
34
|
+
const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
35
|
+
const now = Math.floor((options.now?.() ?? Date.now()) / 1000);
|
|
36
|
+
// Rejected in both directions. A timestamp far in the future is not a clock
|
|
37
|
+
// problem to be generous about — it is someone choosing the timestamp.
|
|
38
|
+
if (Math.abs(now - parsed.timestamp) > tolerance)
|
|
39
|
+
return false;
|
|
40
|
+
const expected = (0, node_crypto_1.createHmac)('sha256', secret)
|
|
41
|
+
.update(`${parsed.timestamp}.${rawBody}`)
|
|
42
|
+
.digest('hex');
|
|
43
|
+
return safeEqual(expected, parsed.signature);
|
|
44
|
+
}
|
|
45
|
+
/** `t=1699999999,v1=abc…` */
|
|
46
|
+
function parseSignatureHeader(header) {
|
|
47
|
+
let timestamp = null;
|
|
48
|
+
let signature = null;
|
|
49
|
+
for (const part of header.split(',')) {
|
|
50
|
+
const [key, value] = part.trim().split('=');
|
|
51
|
+
if (key === 't') {
|
|
52
|
+
const parsedTimestamp = Number.parseInt(value, 10);
|
|
53
|
+
if (Number.isFinite(parsedTimestamp))
|
|
54
|
+
timestamp = parsedTimestamp;
|
|
55
|
+
}
|
|
56
|
+
else if (key === 'v1') {
|
|
57
|
+
signature = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return timestamp !== null && signature ? { timestamp, signature } : null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* `===` on a signature leaks, through timing, how many leading characters an
|
|
64
|
+
* attacker guessed right — which is enough to reconstruct it one character at
|
|
65
|
+
* a time.
|
|
66
|
+
*/
|
|
67
|
+
function safeEqual(a, b) {
|
|
68
|
+
const left = Buffer.from(a, 'utf8');
|
|
69
|
+
const right = Buffer.from(b, 'utf8');
|
|
70
|
+
if (left.length !== right.length)
|
|
71
|
+
return false;
|
|
72
|
+
return (0, node_crypto_1.timingSafeEqual)(left, right);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":";;AAiCA,wDAsBC;AAvDD,6CAAyD;AAEzD;;;;;;;GAOG;AACH,MAAM,yBAAyB,GAAG,GAAG,CAAA;AASrC;;;;;;;;;;;;;GAaG;AACH,SAAgB,sBAAsB,CACpC,OAAe,EACf,eAA0C,EAC1C,MAAc,EACd,UAAyB,EAAE;IAE3B,IAAI,CAAC,eAAe,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAE7C,MAAM,MAAM,GAAG,oBAAoB,CAAC,eAAe,CAAC,CAAA;IACpD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,IAAI,yBAAyB,CAAA;IACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAC9D,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,SAAS;QAAE,OAAO,KAAK,CAAA;IAE9D,MAAM,QAAQ,GAAG,IAAA,wBAAU,EAAC,QAAQ,EAAE,MAAM,CAAC;SAC1C,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,IAAI,OAAO,EAAE,CAAC;SACxC,MAAM,CAAC,KAAK,CAAC,CAAA;IAEhB,OAAO,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;AAC9C,CAAC;AAED,6BAA6B;AAC7B,SAAS,oBAAoB,CAAC,MAAc;IAC1C,IAAI,SAAS,GAAkB,IAAI,CAAA;IACnC,IAAI,SAAS,GAAkB,IAAI,CAAA;IAEnC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3C,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YAChB,MAAM,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;YAClD,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE,SAAS,GAAG,eAAe,CAAA;QACnE,CAAC;aAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACxB,SAAS,GAAG,KAAK,CAAA;QACnB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC1E,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACpC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAC9C,OAAO,IAAA,6BAAe,EAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.spec.d.ts","sourceRoot":"","sources":["../src/webhooks.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
|
+
const vitest_1 = require("vitest");
|
|
5
|
+
const webhooks_1 = require("./webhooks");
|
|
6
|
+
/**
|
|
7
|
+
* This function is the only thing standing between an app's webhook handler and
|
|
8
|
+
* anyone on the internet who knows its URL. Every case below is a way that
|
|
9
|
+
* boundary gets crossed by an implementation that "looks right".
|
|
10
|
+
*/
|
|
11
|
+
const SECRET = 'whsec_test';
|
|
12
|
+
const BODY = '{"event":"orders/create","data":{"id":"ord_1"}}';
|
|
13
|
+
function sign(body, timestamp, secret = SECRET) {
|
|
14
|
+
const signature = (0, node_crypto_1.createHmac)('sha256', secret).update(`${timestamp}.${body}`).digest('hex');
|
|
15
|
+
return `t=${timestamp},v1=${signature}`;
|
|
16
|
+
}
|
|
17
|
+
const NOW = 1_700_000_000_000;
|
|
18
|
+
const now = () => NOW;
|
|
19
|
+
const nowSeconds = Math.floor(NOW / 1000);
|
|
20
|
+
(0, vitest_1.describe)('verifyWebhookSignature', () => {
|
|
21
|
+
(0, vitest_1.it)('accepts a correctly signed, fresh payload', () => {
|
|
22
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, sign(BODY, nowSeconds), SECRET, { now })).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)('rejects a body that changed after signing', () => {
|
|
25
|
+
const header = sign(BODY, nowSeconds);
|
|
26
|
+
const tampered = BODY.replace('ord_1', 'ord_999');
|
|
27
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(tampered, header, SECRET, { now })).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
(0, vitest_1.it)('rejects a signature made with a different secret', () => {
|
|
30
|
+
const header = sign(BODY, nowSeconds, 'whsec_someone_elses');
|
|
31
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.describe)('replay', () => {
|
|
34
|
+
(0, vitest_1.it)('rejects a payload older than the tolerance', () => {
|
|
35
|
+
// Without this, a captured delivery verifies forever.
|
|
36
|
+
const header = sign(BODY, nowSeconds - 301);
|
|
37
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)('accepts one just inside the tolerance', () => {
|
|
40
|
+
const header = sign(BODY, nowSeconds - 299);
|
|
41
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
(0, vitest_1.it)('rejects a timestamp in the future', () => {
|
|
44
|
+
// Not a clock problem to be generous about — a future timestamp means
|
|
45
|
+
// someone chose it.
|
|
46
|
+
const header = sign(BODY, nowSeconds + 301);
|
|
47
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
(0, vitest_1.describe)('malformed input is rejected, never thrown on', () => {
|
|
51
|
+
// A handler that 500s on a malformed header tells an attacker their probe
|
|
52
|
+
// reached real code. Every one of these must simply be false.
|
|
53
|
+
const bad = [
|
|
54
|
+
['missing header', null],
|
|
55
|
+
['empty header', ''],
|
|
56
|
+
['no timestamp', 'v1=abc'],
|
|
57
|
+
['no signature', `t=${nowSeconds}`],
|
|
58
|
+
['non-numeric timestamp', 't=not-a-number,v1=abc'],
|
|
59
|
+
['garbage', 'nonsense'],
|
|
60
|
+
];
|
|
61
|
+
vitest_1.it.each(bad)('rejects %s', (_label, header) => {
|
|
62
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, header, SECRET, { now })).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.it)('rejects when no secret is configured', () => {
|
|
65
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, sign(BODY, nowSeconds), '', { now })).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
(0, vitest_1.it)('rejects a signature of the right shape but the wrong length', () => {
|
|
69
|
+
// Guards the constant-time compare: a length mismatch must return false,
|
|
70
|
+
// not throw, or the throw itself becomes the timing signal.
|
|
71
|
+
(0, vitest_1.expect)((0, webhooks_1.verifyWebhookSignature)(BODY, `t=${nowSeconds},v1=abc`, SECRET, { now })).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=webhooks.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.spec.js","sourceRoot":"","sources":["../src/webhooks.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanqory/plugin-sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/tanqory/tanqory-platform-packages.git",
|
|
7
|
+
"directory": "packages/tools/plugin-sdk"
|
|
8
|
+
},
|
|
9
|
+
"description": "Build a Tanqory marketplace app. Server-side token exchange and REST client, webhook signature verification, a manifest schema, and a React hook that boots an embedded app inside the Tanqory dashboard.",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"tanqory",
|
|
12
|
+
"ecommerce",
|
|
13
|
+
"app",
|
|
14
|
+
"plugin",
|
|
15
|
+
"sdk"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"registry": "https://npm.pkg.github.com"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Tanqory",
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./client": {
|
|
30
|
+
"types": "./dist/client/index.d.ts",
|
|
31
|
+
"default": "./dist/client/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json",
|
|
40
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
41
|
+
"clean": "rm -rf dist",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"test": "vitest run"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@tanqory/app-bridge": "^0.2.1"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"react": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^20.0.0",
|
|
58
|
+
"@types/react": "^18.0.0",
|
|
59
|
+
"react": "^18.0.0",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"vitest": "^2.0.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=20"
|
|
65
|
+
},
|
|
66
|
+
"sideEffects": false
|
|
67
|
+
}
|