@oneaddress/setup 1.1.8 → 1.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 +2 -2
- package/dist/index.js +63 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,8 +48,8 @@ From the [Partner Portal](https://partners.oneaddress.io) → My Profile → Int
|
|
|
48
48
|
## Documentation
|
|
49
49
|
|
|
50
50
|
- [Partner Portal](https://partners.oneaddress.io)
|
|
51
|
-
- [Protocol Specification](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/
|
|
52
|
-
- [Service Level Objectives](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/
|
|
51
|
+
- [Protocol Specification](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integrations/SPEC.md)
|
|
52
|
+
- [Service Level Objectives](https://github.com/oneaddress-io/oneaddress-app/blob/main/docs/integrations/SLOS.md)
|
|
53
53
|
|
|
54
54
|
## Issues
|
|
55
55
|
|
package/dist/index.js
CHANGED
|
@@ -835,7 +835,7 @@ var _R = ["\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588"
|
|
|
835
835
|
var _S = [" \u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588 "];
|
|
836
836
|
var ONE_ROWS = Array.from({ length: 7 }, (_2, i) => [_O[i], _N[i], _E[i]].join(" "));
|
|
837
837
|
var ADDR_ROWS = Array.from({ length: 7 }, (_2, i) => [_A[i], _D2[i], _D2[i], _R[i], _E[i], _S[i], _S[i]].join(" "));
|
|
838
|
-
var WIZARD_VERSION = true ? "1.
|
|
838
|
+
var WIZARD_VERSION = true ? "1.2.0" : "?";
|
|
839
839
|
function printHeader() {
|
|
840
840
|
const BAR_LEN = 85;
|
|
841
841
|
const TOP_BAR = fn("\u250C") + dm("\u2500".repeat(BAR_LEN)) + fn("\u2510");
|
|
@@ -860,7 +860,7 @@ function printHeader() {
|
|
|
860
860
|
console.log("");
|
|
861
861
|
}
|
|
862
862
|
function stripAnsi(s) {
|
|
863
|
-
return s.
|
|
863
|
+
return s.replaceAll(/\x1b\[[0-9;]*m/g, "");
|
|
864
864
|
}
|
|
865
865
|
|
|
866
866
|
// src/scaffold.ts
|
|
@@ -1080,9 +1080,24 @@ export async function verifyAddress(customer: Customer, address: Address): Promi
|
|
|
1080
1080
|
* \u2551 To wire up your database: edit src/store.ts instead. \u2551
|
|
1081
1081
|
* \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
1082
1082
|
*/
|
|
1083
|
+
// Node.js 22.5+ required for the built-in node:sqlite module used by src/store.ts.
|
|
1084
|
+
// Fail fast with a clear message rather than a cryptic ERR_UNSUPPORTED_FEATURE later.
|
|
1085
|
+
const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
|
|
1086
|
+
if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 5)) {
|
|
1087
|
+
console.error(\`[startup] Node.js 22.5+ is required (you are running \${process.version}).\`);
|
|
1088
|
+
console.error('[startup] Upgrade Node.js: https://nodejs.org/en/download');
|
|
1089
|
+
process.exit(1);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1083
1092
|
import 'dotenv/config';
|
|
1084
1093
|
import express, { Request, Response } from 'express';
|
|
1085
|
-
import {
|
|
1094
|
+
import {
|
|
1095
|
+
verifySignature,
|
|
1096
|
+
decryptAddress,
|
|
1097
|
+
decryptSession,
|
|
1098
|
+
isFreshTimestamp,
|
|
1099
|
+
type SessionKeyShare,
|
|
1100
|
+
} from '@oneaddress/partner-sdk';
|
|
1086
1101
|
import { saveAddress, verifyAddress } from './store.js';
|
|
1087
1102
|
|
|
1088
1103
|
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET ?? '';
|
|
@@ -1128,23 +1143,43 @@ app.post('/webhook', async (req: Request, res: Response) => {
|
|
|
1128
1143
|
}
|
|
1129
1144
|
if (dispatch) seenDispatches.add(dispatch);
|
|
1130
1145
|
|
|
1131
|
-
// 5. Decrypt address payload
|
|
1132
|
-
|
|
1146
|
+
// 5. Decrypt address payload. Two shapes possible:
|
|
1147
|
+
// (a) D5 (new, 2026+): body.session_envelope + body.session_key_share.
|
|
1148
|
+
// (b) Legacy: body.address_encrypted.
|
|
1149
|
+
// A webhook will carry exactly one shape. If you've registered multiple
|
|
1150
|
+
// versioned keys (D5 supports key rotation with 90-day overlap), look up
|
|
1151
|
+
// the right private key by session_key_share.key_id from your key store.
|
|
1152
|
+
// This scaffold uses a single PARTNER_PRIVATE_KEY_PEM for simplicity \u2014
|
|
1153
|
+
// production deployments should hold a Map<key_id, privateKeyPem>.
|
|
1154
|
+
let address: Record<string, unknown>;
|
|
1155
|
+
|
|
1156
|
+
const sessionEnvelope = typeof body.session_envelope === 'string' ? body.session_envelope : null;
|
|
1157
|
+
const sessionKeyShare = body.session_key_share as SessionKeyShare | undefined;
|
|
1158
|
+
const legacyPayload = body.address_encrypted as {
|
|
1133
1159
|
ephemeralPublicKey: string; iv: string; ciphertext: string; hkdfSalt?: string;
|
|
1134
1160
|
} | null;
|
|
1135
1161
|
|
|
1136
|
-
if (
|
|
1162
|
+
if (sessionEnvelope && sessionKeyShare) {
|
|
1163
|
+
// D5 \u2014 call decryptSession. Wrong key_id / wrong partner_id / tampered
|
|
1164
|
+
// ciphertext all surface as an AES-GCM authentication-tag error.
|
|
1165
|
+
try {
|
|
1166
|
+
const data = await decryptSession(sessionKeyShare, sessionEnvelope, PARTNER_PRIVATE_KEY, PARTNER_ID);
|
|
1167
|
+
address = data.new_address as unknown as Record<string, unknown>;
|
|
1168
|
+
} catch (err) {
|
|
1169
|
+
console.error('[webhook] D5 decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM matches key_id', sessionKeyShare.key_id, ':', err);
|
|
1170
|
+
return res.status(200).json({ ok: true, note: 'Received \u2014 D5 decryption pending key setup' });
|
|
1171
|
+
}
|
|
1172
|
+
} else if (legacyPayload) {
|
|
1173
|
+
try {
|
|
1174
|
+
address = await decryptAddress(legacyPayload, PARTNER_PRIVATE_KEY, PARTNER_ID);
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
console.error('[webhook] Decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM in .env:', err);
|
|
1177
|
+
return res.status(200).json({ ok: true, note: 'Received \u2014 decryption pending key setup' });
|
|
1178
|
+
}
|
|
1179
|
+
} else {
|
|
1137
1180
|
return res.status(200).json({ ok: true, note: 'No encrypted payload' });
|
|
1138
1181
|
}
|
|
1139
1182
|
|
|
1140
|
-
let address: Record<string, unknown>;
|
|
1141
|
-
try {
|
|
1142
|
-
address = await decryptAddress(encPayload, PARTNER_PRIVATE_KEY, PARTNER_ID);
|
|
1143
|
-
} catch (err) {
|
|
1144
|
-
console.error('[webhook] Decryption failed \u2014 check PARTNER_PRIVATE_KEY_PEM in .env:', err);
|
|
1145
|
-
return res.status(200).json({ ok: true, note: 'Received \u2014 decryption pending key setup' });
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
1183
|
const event = body.event as string;
|
|
1149
1184
|
const customer = body.customer as { email?: string; name?: string } | undefined;
|
|
1150
1185
|
const ctx = { email: customer?.email ?? '', name: customer?.name ?? '' };
|
|
@@ -3031,8 +3066,17 @@ async function runConformance(webhookUrl, _partnerId, webhookSecret) {
|
|
|
3031
3066
|
body: validBody,
|
|
3032
3067
|
signal: AbortSignal.timeout(8e3)
|
|
3033
3068
|
});
|
|
3034
|
-
|
|
3035
|
-
|
|
3069
|
+
let bodyOk = true;
|
|
3070
|
+
if (t.expectOk) {
|
|
3071
|
+
try {
|
|
3072
|
+
const json = await res.json();
|
|
3073
|
+
bodyOk = json.ok === true;
|
|
3074
|
+
} catch {
|
|
3075
|
+
bodyOk = false;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
const gotOk = res.ok && bodyOk;
|
|
3079
|
+
const pass = t.expectOk ? gotOk : !res.ok;
|
|
3036
3080
|
if (pass) {
|
|
3037
3081
|
v2.success(` \u2713 ${t.name} (${res.status})`);
|
|
3038
3082
|
passed++;
|
|
@@ -3375,7 +3419,7 @@ function normalisePrivateKey(raw) {
|
|
|
3375
3419
|
if (!(0, import_node_fs4.existsSync)(abs)) return { pem: "", error: `File not found: ${abs}` };
|
|
3376
3420
|
return normalisePrivateKey((0, import_node_fs4.readFileSync)(abs, "utf8"));
|
|
3377
3421
|
}
|
|
3378
|
-
const unescaped = trimmed.
|
|
3422
|
+
const unescaped = trimmed.replaceAll(/\\n/g, "\n");
|
|
3379
3423
|
if (unescaped.includes("-----BEGIN PRIVATE KEY-----")) {
|
|
3380
3424
|
return { pem: unescaped };
|
|
3381
3425
|
}
|
|
@@ -3395,7 +3439,7 @@ function normalisePrivateKey(raw) {
|
|
|
3395
3439
|
if (b64Lines.length === 0) return { pem: "", error: "Could not extract key body from the OneAddress PEM file" };
|
|
3396
3440
|
return wrapAsPkcs8(b64Lines.join(""));
|
|
3397
3441
|
}
|
|
3398
|
-
const b64clean = trimmed.
|
|
3442
|
+
const b64clean = trimmed.replaceAll(/\s+/g, "");
|
|
3399
3443
|
if (/^[A-Za-z0-9+/]+=*$/.test(b64clean) && b64clean.length >= 50) {
|
|
3400
3444
|
return wrapAsPkcs8(b64clean);
|
|
3401
3445
|
}
|
|
@@ -3632,7 +3676,7 @@ Continuing will overwrite it.`
|
|
|
3632
3676
|
`Files in: ${outDir}`
|
|
3633
3677
|
].join("\n");
|
|
3634
3678
|
const nextSteps = [
|
|
3635
|
-
"1. Trigger a live event: update your address at
|
|
3679
|
+
"1. Trigger a live event: update your address at oneaddress.io\n \u2192 src/store.ts will save it to data.db automatically",
|
|
3636
3680
|
"2. View your database: open data.db with DB Browser for SQLite\n (free download at sqlitebrowser.org)",
|
|
3637
3681
|
"3. For production: swap src/store.ts for your real database (Postgres, MySQL, etc.)\n then deploy and update the webhook URL in the portal",
|
|
3638
3682
|
"4. Questions? partners.oneaddress.io or partners@oneaddress.io"
|