@bandeira-tech/b3nd-web 0.2.7 → 0.3.1
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/dist/{chunk-F3W5GZU6.js → chunk-B4VAPGAO.js} +218 -22
- package/dist/{chunk-7O4D7SF6.js → chunk-GWPCZVXV.js} +233 -59
- package/dist/{chunk-GIMIWVPX.js → chunk-O53KW746.js} +53 -2
- package/dist/{chunk-LFUC4ETD.js → chunk-OY4CDOHY.js} +45 -0
- package/dist/{chunk-7U5JDFQW.js → chunk-PZFEKQ7F.js} +24 -0
- package/dist/{chunk-T43IWAQK.js → chunk-UUHVOWVI.js} +29 -0
- package/dist/{client-B0Ekm99R.d.ts → client-C4oQxiDu.d.ts} +147 -8
- package/dist/clients/http/mod.d.ts +3 -1
- package/dist/clients/http/mod.js +1 -1
- package/dist/clients/local-storage/mod.d.ts +2 -1
- package/dist/clients/local-storage/mod.js +1 -1
- package/dist/clients/memory/mod.d.ts +36 -2
- package/dist/clients/memory/mod.js +5 -3
- package/dist/clients/websocket/mod.d.ts +2 -1
- package/dist/clients/websocket/mod.js +1 -1
- package/dist/{core-CgxQpSVM.d.ts → core-ClnuubZw.d.ts} +17 -1
- package/dist/src/mod.web.d.ts +2 -2
- package/dist/src/mod.web.js +10 -10
- package/dist/{types-oQCx3U-_.d.ts → types-uuvn4oKw.d.ts} +36 -2
- package/dist/wallet/mod.d.ts +183 -8
- package/dist/wallet/mod.js +8 -4
- package/dist/wallet-server/adapters/browser.d.ts +3 -3
- package/dist/wallet-server/adapters/browser.js +5 -5
- package/dist/wallet-server/mod.d.ts +12 -6
- package/dist/wallet-server/mod.js +2 -2
- package/package.json +1 -1
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
decrypt,
|
|
6
6
|
encodeHex,
|
|
7
7
|
encrypt,
|
|
8
|
+
verify,
|
|
8
9
|
verifyPayload
|
|
9
10
|
} from "./chunk-JN75UL5C.js";
|
|
10
11
|
import {
|
|
11
12
|
HttpClient
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OY4CDOHY.js";
|
|
13
14
|
|
|
14
15
|
// wallet-server/interfaces.ts
|
|
15
16
|
var defaultLogger = {
|
|
@@ -446,27 +447,42 @@ async function proxyWrite(proxyClient, credentialClient, serverPublicKey, userna
|
|
|
446
447
|
};
|
|
447
448
|
}
|
|
448
449
|
}
|
|
449
|
-
async function proxyRead(proxyClient,
|
|
450
|
+
async function proxyRead(proxyClient, credentialClient, serverPublicKey, username, serverEncryptionPrivateKeyPem, uri) {
|
|
450
451
|
try {
|
|
451
|
-
const
|
|
452
|
+
const accountKey = await loadUserAccountKey(
|
|
453
|
+
credentialClient,
|
|
454
|
+
serverPublicKey,
|
|
455
|
+
username,
|
|
456
|
+
serverEncryptionPrivateKeyPem
|
|
457
|
+
);
|
|
458
|
+
const resolvedUri = uri.replace(/:key/g, accountKey.publicKeyHex);
|
|
459
|
+
const result = await proxyClient.read(resolvedUri);
|
|
452
460
|
if (!result.success) {
|
|
453
461
|
return {
|
|
454
462
|
success: false,
|
|
463
|
+
uri: resolvedUri,
|
|
455
464
|
error: result.error || "Read failed"
|
|
456
465
|
};
|
|
457
466
|
}
|
|
458
467
|
const response = {
|
|
459
468
|
success: true,
|
|
469
|
+
uri: resolvedUri,
|
|
460
470
|
record: result.record
|
|
461
471
|
};
|
|
462
|
-
if (
|
|
472
|
+
if (result.record?.data) {
|
|
463
473
|
try {
|
|
464
474
|
const data = result.record.data;
|
|
465
475
|
if (typeof data === "object" && data.payload && typeof data.payload === "object") {
|
|
466
476
|
const payload = data.payload;
|
|
467
477
|
if (payload.data && payload.nonce && payload.ephemeralPublicKey) {
|
|
478
|
+
const userEncryptionKey = await loadUserEncryptionKey(
|
|
479
|
+
credentialClient,
|
|
480
|
+
serverPublicKey,
|
|
481
|
+
username,
|
|
482
|
+
serverEncryptionPrivateKeyPem
|
|
483
|
+
);
|
|
468
484
|
const privateKey = await pemToCryptoKey(
|
|
469
|
-
|
|
485
|
+
userEncryptionKey.privateKeyPem,
|
|
470
486
|
"X25519"
|
|
471
487
|
);
|
|
472
488
|
const encryptedPayload = {
|
|
@@ -491,10 +507,94 @@ async function proxyRead(proxyClient, uri, serverEncryptionPrivateKeyPem) {
|
|
|
491
507
|
} catch (error) {
|
|
492
508
|
return {
|
|
493
509
|
success: false,
|
|
510
|
+
uri,
|
|
494
511
|
error: `Proxy read failed: ${error instanceof Error ? error.message : String(error)}`
|
|
495
512
|
};
|
|
496
513
|
}
|
|
497
514
|
}
|
|
515
|
+
async function proxyReadMulti(proxyClient, credentialClient, serverPublicKey, username, serverEncryptionPrivateKeyPem, uris) {
|
|
516
|
+
if (uris.length > 50) {
|
|
517
|
+
return {
|
|
518
|
+
success: false,
|
|
519
|
+
results: [],
|
|
520
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length },
|
|
521
|
+
error: "Maximum 50 URIs per request"
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
const [accountKey, userEncryptionKey] = await Promise.all([
|
|
526
|
+
loadUserAccountKey(
|
|
527
|
+
credentialClient,
|
|
528
|
+
serverPublicKey,
|
|
529
|
+
username,
|
|
530
|
+
serverEncryptionPrivateKeyPem
|
|
531
|
+
),
|
|
532
|
+
loadUserEncryptionKey(
|
|
533
|
+
credentialClient,
|
|
534
|
+
serverPublicKey,
|
|
535
|
+
username,
|
|
536
|
+
serverEncryptionPrivateKeyPem
|
|
537
|
+
)
|
|
538
|
+
]);
|
|
539
|
+
const resolvedUris = uris.map(
|
|
540
|
+
(uri) => uri.replace(/:key/g, accountKey.publicKeyHex)
|
|
541
|
+
);
|
|
542
|
+
const multiResult = await proxyClient.readMulti(resolvedUris);
|
|
543
|
+
const privateKey = await pemToCryptoKey(
|
|
544
|
+
userEncryptionKey.privateKeyPem,
|
|
545
|
+
"X25519"
|
|
546
|
+
);
|
|
547
|
+
const results = await Promise.all(
|
|
548
|
+
multiResult.results.map(async (item, i) => {
|
|
549
|
+
const originalUri = uris[i];
|
|
550
|
+
if (!item.success) {
|
|
551
|
+
return {
|
|
552
|
+
uri: originalUri,
|
|
553
|
+
success: false,
|
|
554
|
+
error: "error" in item ? item.error : "Read failed"
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
const result = {
|
|
558
|
+
uri: originalUri,
|
|
559
|
+
success: true,
|
|
560
|
+
record: "record" in item ? item.record : void 0
|
|
561
|
+
};
|
|
562
|
+
if (result.record?.data) {
|
|
563
|
+
try {
|
|
564
|
+
const data = result.record.data;
|
|
565
|
+
if (typeof data === "object" && data.payload && typeof data.payload === "object") {
|
|
566
|
+
const payload = data.payload;
|
|
567
|
+
if (payload.data && payload.nonce && payload.ephemeralPublicKey) {
|
|
568
|
+
const encryptedPayload = {
|
|
569
|
+
data: payload.data,
|
|
570
|
+
nonce: payload.nonce,
|
|
571
|
+
ephemeralPublicKey: payload.ephemeralPublicKey
|
|
572
|
+
};
|
|
573
|
+
const decrypted = await decrypt(encryptedPayload, privateKey);
|
|
574
|
+
return { ...result, decrypted };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return result;
|
|
581
|
+
})
|
|
582
|
+
);
|
|
583
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
584
|
+
return {
|
|
585
|
+
success: succeeded > 0,
|
|
586
|
+
results,
|
|
587
|
+
summary: { total: uris.length, succeeded, failed: uris.length - succeeded }
|
|
588
|
+
};
|
|
589
|
+
} catch (error) {
|
|
590
|
+
return {
|
|
591
|
+
success: false,
|
|
592
|
+
results: [],
|
|
593
|
+
summary: { total: uris.length, succeeded: 0, failed: uris.length },
|
|
594
|
+
error: `Proxy read-multi failed: ${error instanceof Error ? error.message : String(error)}`
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
498
598
|
|
|
499
599
|
// wallet-server/auth.ts
|
|
500
600
|
function generateSalt() {
|
|
@@ -581,7 +681,7 @@ async function createUser(client, serverPublicKey, username, password, serverIde
|
|
|
581
681
|
);
|
|
582
682
|
return { salt, hash };
|
|
583
683
|
}
|
|
584
|
-
async function authenticateUser(client, serverPublicKey, username, password,
|
|
684
|
+
async function authenticateUser(client, serverPublicKey, username, password, serverEncryptionPrivateKeyPem, appScope, logger) {
|
|
585
685
|
const passwordPath = await deriveObfuscatedPath(
|
|
586
686
|
serverPublicKey,
|
|
587
687
|
username,
|
|
@@ -613,7 +713,6 @@ async function changePassword(client, serverPublicKey, username, oldPassword, ne
|
|
|
613
713
|
serverPublicKey,
|
|
614
714
|
username,
|
|
615
715
|
oldPassword,
|
|
616
|
-
serverIdentityPublicKeyHex,
|
|
617
716
|
serverEncryptionPrivateKeyPem,
|
|
618
717
|
appScope
|
|
619
718
|
);
|
|
@@ -987,7 +1086,6 @@ var PasswordCredentialHandler = class {
|
|
|
987
1086
|
context.serverPublicKey,
|
|
988
1087
|
username,
|
|
989
1088
|
password,
|
|
990
|
-
context.serverIdentityPublicKeyHex,
|
|
991
1089
|
context.serverEncryptionPrivateKeyPem,
|
|
992
1090
|
context.appKey,
|
|
993
1091
|
context.logger
|
|
@@ -1294,13 +1392,42 @@ var WalletServerCore = class {
|
|
|
1294
1392
|
async writeBootstrapState(path, state) {
|
|
1295
1393
|
await this.storage.writeTextFile(path, JSON.stringify(state, null, 2));
|
|
1296
1394
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1395
|
+
/**
|
|
1396
|
+
* Validate that a session is approved by the app.
|
|
1397
|
+
* Sessions are keypairs - the sessionPubkey is used directly as the identifier.
|
|
1398
|
+
* App approves sessions by writing 1 to mutable://accounts/{appKey}/sessions/{sessionPubkey}
|
|
1399
|
+
*
|
|
1400
|
+
* @param appKey - The app's public key
|
|
1401
|
+
* @param sessionPubkey - The session's public key (hex encoded)
|
|
1402
|
+
* @returns { valid: true } if approved, { valid: false, reason } if not
|
|
1403
|
+
*/
|
|
1404
|
+
async sessionExists(appKey, sessionPubkey) {
|
|
1405
|
+
const uri = `mutable://accounts/${appKey}/sessions/${sessionPubkey}`;
|
|
1302
1406
|
const res = await this.proxyClient.read(uri);
|
|
1303
|
-
|
|
1407
|
+
if (!res.success) {
|
|
1408
|
+
return { valid: false, reason: "session_not_approved" };
|
|
1409
|
+
}
|
|
1410
|
+
if (res.record?.data === 1) {
|
|
1411
|
+
return { valid: true };
|
|
1412
|
+
}
|
|
1413
|
+
if (res.record?.data === 0) {
|
|
1414
|
+
return { valid: false, reason: "session_revoked" };
|
|
1415
|
+
}
|
|
1416
|
+
return { valid: false, reason: "invalid_session_status" };
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Verify that a login request signature is valid for the given session pubkey.
|
|
1420
|
+
* The signature should be over the stringified login payload (without the signature field).
|
|
1421
|
+
* Uses SDK crypto for consistent verification across the codebase.
|
|
1422
|
+
*/
|
|
1423
|
+
async verifySessionSignature(sessionPubkey, signature, payload) {
|
|
1424
|
+
try {
|
|
1425
|
+
const { sessionSignature: _, ...signedPayload } = payload;
|
|
1426
|
+
return await verify(sessionPubkey, signature, signedPayload);
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
this.logger.error("Session signature verification failed:", error);
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1304
1431
|
}
|
|
1305
1432
|
createApp() {
|
|
1306
1433
|
const app = new Hono();
|
|
@@ -1402,12 +1529,33 @@ var WalletServerCore = class {
|
|
|
1402
1529
|
if (!appKey) {
|
|
1403
1530
|
return c.json({ success: false, error: "appKey is required" }, 400);
|
|
1404
1531
|
}
|
|
1532
|
+
if (!payload.sessionPubkey) {
|
|
1533
|
+
return c.json({ success: false, error: "sessionPubkey is required" }, 400);
|
|
1534
|
+
}
|
|
1535
|
+
if (!payload.sessionSignature) {
|
|
1536
|
+
return c.json({ success: false, error: "sessionSignature is required" }, 400);
|
|
1537
|
+
}
|
|
1405
1538
|
if (!payload.type) {
|
|
1406
1539
|
return c.json({
|
|
1407
1540
|
success: false,
|
|
1408
1541
|
error: `type is required. Supported: ${getSupportedCredentialTypes().join(", ")}`
|
|
1409
1542
|
}, 400);
|
|
1410
1543
|
}
|
|
1544
|
+
const signatureValid = await this.verifySessionSignature(
|
|
1545
|
+
payload.sessionPubkey,
|
|
1546
|
+
payload.sessionSignature,
|
|
1547
|
+
payload
|
|
1548
|
+
);
|
|
1549
|
+
if (!signatureValid) {
|
|
1550
|
+
return c.json({ success: false, error: "Invalid session signature" }, 401);
|
|
1551
|
+
}
|
|
1552
|
+
const sessionResult = await this.sessionExists(appKey, payload.sessionPubkey);
|
|
1553
|
+
if (!sessionResult.valid) {
|
|
1554
|
+
return c.json({
|
|
1555
|
+
success: false,
|
|
1556
|
+
error: sessionResult.reason === "session_revoked" ? "Session has been revoked" : sessionResult.reason === "session_not_approved" ? "Session not approved by app" : "Invalid session"
|
|
1557
|
+
}, 401);
|
|
1558
|
+
}
|
|
1411
1559
|
const handler = getCredentialHandler(payload.type);
|
|
1412
1560
|
let googleClientId;
|
|
1413
1561
|
if (payload.type === "google") {
|
|
@@ -1469,8 +1617,11 @@ var WalletServerCore = class {
|
|
|
1469
1617
|
if (!appKey) {
|
|
1470
1618
|
return c.json({ success: false, error: "appKey is required" }, 400);
|
|
1471
1619
|
}
|
|
1472
|
-
if (!payload.
|
|
1473
|
-
return c.json({ success: false, error: "
|
|
1620
|
+
if (!payload.sessionPubkey) {
|
|
1621
|
+
return c.json({ success: false, error: "sessionPubkey is required" }, 400);
|
|
1622
|
+
}
|
|
1623
|
+
if (!payload.sessionSignature) {
|
|
1624
|
+
return c.json({ success: false, error: "sessionSignature is required" }, 400);
|
|
1474
1625
|
}
|
|
1475
1626
|
if (!payload.type) {
|
|
1476
1627
|
return c.json({
|
|
@@ -1478,8 +1629,20 @@ var WalletServerCore = class {
|
|
|
1478
1629
|
error: `type is required. Supported: ${getSupportedCredentialTypes().join(", ")}`
|
|
1479
1630
|
}, 400);
|
|
1480
1631
|
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1632
|
+
const signatureValid = await this.verifySessionSignature(
|
|
1633
|
+
payload.sessionPubkey,
|
|
1634
|
+
payload.sessionSignature,
|
|
1635
|
+
payload
|
|
1636
|
+
);
|
|
1637
|
+
if (!signatureValid) {
|
|
1638
|
+
return c.json({ success: false, error: "Invalid session signature" }, 401);
|
|
1639
|
+
}
|
|
1640
|
+
const sessionResult = await this.sessionExists(appKey, payload.sessionPubkey);
|
|
1641
|
+
if (!sessionResult.valid) {
|
|
1642
|
+
return c.json({
|
|
1643
|
+
success: false,
|
|
1644
|
+
error: sessionResult.reason === "session_revoked" ? "Session has been revoked" : sessionResult.reason === "session_not_approved" ? "Session not approved by app" : "Invalid session"
|
|
1645
|
+
}, 401);
|
|
1483
1646
|
}
|
|
1484
1647
|
const handler = getCredentialHandler(payload.type);
|
|
1485
1648
|
let googleClientId;
|
|
@@ -1704,22 +1867,25 @@ var WalletServerCore = class {
|
|
|
1704
1867
|
return c.json({ success: false, error: "Authorization required" }, 401);
|
|
1705
1868
|
}
|
|
1706
1869
|
const token = authHeader.substring(7);
|
|
1707
|
-
await verifyJwt(token, this.config.jwtSecret);
|
|
1870
|
+
const payload = await verifyJwt(token, this.config.jwtSecret);
|
|
1708
1871
|
const uri = c.req.query("uri");
|
|
1709
1872
|
if (!uri) {
|
|
1710
1873
|
return c.json({ success: false, error: "uri query parameter is required" }, 400);
|
|
1711
1874
|
}
|
|
1712
1875
|
const result = await proxyRead(
|
|
1713
1876
|
this.proxyClient,
|
|
1714
|
-
|
|
1715
|
-
|
|
1877
|
+
this.credentialClient,
|
|
1878
|
+
serverPublicKey,
|
|
1879
|
+
payload.username,
|
|
1880
|
+
serverEncryptionPrivateKeyPem,
|
|
1881
|
+
uri
|
|
1716
1882
|
);
|
|
1717
1883
|
if (!result.success) {
|
|
1718
1884
|
return c.json({ success: false, error: result.error }, 400);
|
|
1719
1885
|
}
|
|
1720
1886
|
return c.json({
|
|
1721
1887
|
success: true,
|
|
1722
|
-
uri,
|
|
1888
|
+
uri: result.uri,
|
|
1723
1889
|
record: result.record,
|
|
1724
1890
|
decrypted: result.decrypted
|
|
1725
1891
|
});
|
|
@@ -1732,6 +1898,36 @@ var WalletServerCore = class {
|
|
|
1732
1898
|
return c.json({ success: false, error: message }, 500);
|
|
1733
1899
|
}
|
|
1734
1900
|
});
|
|
1901
|
+
app.post("/api/v1/proxy/read-multi", async (c) => {
|
|
1902
|
+
try {
|
|
1903
|
+
const authHeader = c.req.header("Authorization");
|
|
1904
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
1905
|
+
return c.json({ success: false, error: "Authorization required" }, 401);
|
|
1906
|
+
}
|
|
1907
|
+
const token = authHeader.substring(7);
|
|
1908
|
+
const payload = await verifyJwt(token, this.config.jwtSecret);
|
|
1909
|
+
const body = await c.req.json();
|
|
1910
|
+
if (!Array.isArray(body.uris)) {
|
|
1911
|
+
return c.json({ success: false, error: "uris must be an array" }, 400);
|
|
1912
|
+
}
|
|
1913
|
+
const result = await proxyReadMulti(
|
|
1914
|
+
this.proxyClient,
|
|
1915
|
+
this.credentialClient,
|
|
1916
|
+
serverPublicKey,
|
|
1917
|
+
payload.username,
|
|
1918
|
+
serverEncryptionPrivateKeyPem,
|
|
1919
|
+
body.uris
|
|
1920
|
+
);
|
|
1921
|
+
return c.json(result);
|
|
1922
|
+
} catch (error) {
|
|
1923
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1924
|
+
if (message.includes("expired")) {
|
|
1925
|
+
return c.json({ success: false, error: message }, 401);
|
|
1926
|
+
}
|
|
1927
|
+
this.logger.error("Proxy read-multi error:", error);
|
|
1928
|
+
return c.json({ success: false, error: message }, 500);
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1735
1931
|
return app;
|
|
1736
1932
|
}
|
|
1737
1933
|
};
|