@tinycloud/node-sdk 3.8.0-beta.2 → 3.8.0-beta.21
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 +6 -0
- package/dist/{TinyCloudNode-B6VOBrCb.d.ts → TinyCloudNode-DaD5vJhL.d.cts} +49 -6
- package/dist/{TinyCloudNode-CVO41O12.d.cts → TinyCloudNode-k3TbBwwj.d.ts} +49 -6
- package/dist/{WasmKeyProvider-5Ee7fcAw.d.cts → WasmKeyProvider-BKCPRC-c.d.cts} +21 -1
- package/dist/{WasmKeyProvider-5Ee7fcAw.d.ts → WasmKeyProvider-BKCPRC-c.d.ts} +21 -1
- package/dist/{authority-DGi1hIMK.d.ts → authority-C_fWaxAu.d.ts} +1 -1
- package/dist/{authority-DgVV80bI.d.cts → authority-DJdbZM6k.d.cts} +1 -1
- package/dist/core.cjs +375 -40
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +5 -4
- package/dist/core.d.ts +5 -4
- package/dist/core.js +370 -35
- package/dist/core.js.map +1 -1
- package/dist/credentials-core.d.cts +1 -1
- package/dist/credentials-core.d.ts +1 -1
- package/dist/credentials.d.cts +1 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/{delegation-0LKZkmP2.d.cts → delegation-Ckc3UETl.d.cts} +1 -1
- package/dist/{delegation-0LKZkmP2.d.ts → delegation-Ckc3UETl.d.ts} +1 -1
- package/dist/encryption-keys.d.cts +2 -2
- package/dist/encryption-keys.d.ts +2 -2
- package/dist/index.cjs +343 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +333 -38
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.cjs +3 -0
- package/dist/replication/authority.cjs.map +1 -1
- package/dist/replication/authority.d.cts +4 -3
- package/dist/replication/authority.d.ts +4 -3
- package/dist/replication/authority.js +3 -0
- package/dist/replication/authority.js.map +1 -1
- package/dist/replication/runtime.cjs +1 -0
- package/dist/replication/runtime.cjs.map +1 -1
- package/dist/replication/runtime.js +1 -0
- package/dist/replication/runtime.js.map +1 -1
- package/dist/share-receive.cjs +775 -0
- package/dist/share-receive.cjs.map +1 -0
- package/dist/share-receive.d.cts +32 -0
- package/dist/share-receive.d.ts +32 -0
- package/dist/share-receive.js +777 -0
- package/dist/share-receive.js.map +1 -0
- package/package.json +11 -4
package/dist/core.js
CHANGED
|
@@ -242,10 +242,10 @@ import {
|
|
|
242
242
|
activateSessionWithHost,
|
|
243
243
|
checkNodeInfo,
|
|
244
244
|
AutoApproveSpaceCreationHandler,
|
|
245
|
+
ManifestValidationError,
|
|
245
246
|
DEFAULT_MANIFEST_SPACE,
|
|
246
247
|
ENCRYPTION_PERMISSION_SERVICE,
|
|
247
248
|
composeManifestRequest,
|
|
248
|
-
parseNetworkId,
|
|
249
249
|
principalDidEquals,
|
|
250
250
|
resourceCapabilitiesToAbilitiesMap,
|
|
251
251
|
resourceCapabilitiesToSpaceAbilitiesMap,
|
|
@@ -273,6 +273,44 @@ import {
|
|
|
273
273
|
HOOKS,
|
|
274
274
|
ENCRYPTION
|
|
275
275
|
} from "@tinycloud/sdk-core";
|
|
276
|
+
import { parseEncryptionResource } from "@tinycloud/sdk-services/encryption/resource";
|
|
277
|
+
import { parseNetworkId } from "@tinycloud/sdk-services";
|
|
278
|
+
|
|
279
|
+
// src/authorization/appKeyHostBinding.ts
|
|
280
|
+
var DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
281
|
+
var IPV4 = /^\d+\.\d+\.\d+\.\d+$/;
|
|
282
|
+
var APP_ID = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
283
|
+
function appKeyHostBinding(domain, appId) {
|
|
284
|
+
if (!APP_ID.test(appId)) return false;
|
|
285
|
+
if (domain.endsWith(":")) return false;
|
|
286
|
+
let url;
|
|
287
|
+
try {
|
|
288
|
+
url = new URL(`https://${domain}`);
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "") return false;
|
|
293
|
+
if (url.pathname !== "/" || url.search !== "" || url.hash !== "") return false;
|
|
294
|
+
const host = url.hostname;
|
|
295
|
+
if (host.startsWith("[") || host.endsWith(".") || IPV4.test(host)) return false;
|
|
296
|
+
const labels = host.split(".");
|
|
297
|
+
if (labels.length < 2 || labels[labels.length - 1] === "localhost") return false;
|
|
298
|
+
if (!labels.every((label) => DOMAIN_LABEL.test(label))) return false;
|
|
299
|
+
const reversed = labels.reverse().join(".");
|
|
300
|
+
return appId === reversed || appId.startsWith(`${reversed}.`);
|
|
301
|
+
}
|
|
302
|
+
function isAppKeyHostBound(keysAppId, origin) {
|
|
303
|
+
if (typeof origin === "string" && !origin.includes("://")) {
|
|
304
|
+
return appKeyHostBinding(origin, keysAppId);
|
|
305
|
+
}
|
|
306
|
+
let url;
|
|
307
|
+
try {
|
|
308
|
+
url = typeof origin === "string" ? new URL(origin) : origin;
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
return appKeyHostBinding(url.host, keysAppId);
|
|
313
|
+
}
|
|
276
314
|
|
|
277
315
|
// src/replication/authority-sign-in.ts
|
|
278
316
|
import {
|
|
@@ -410,6 +448,7 @@ function cloneAbilitiesMap(actions) {
|
|
|
410
448
|
var NodeUserAuthorization = class {
|
|
411
449
|
constructor(config) {
|
|
412
450
|
this.activeSessionKeyId = "default";
|
|
451
|
+
this.preparedSessionCounter = 0;
|
|
413
452
|
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
414
453
|
this.sessionGeneration = 0;
|
|
415
454
|
/**
|
|
@@ -766,6 +805,23 @@ var NodeUserAuthorization = class {
|
|
|
766
805
|
);
|
|
767
806
|
return this._capabilityRequest;
|
|
768
807
|
}
|
|
808
|
+
async refreshNamedKeyFeatureForSignIn(address, chainId) {
|
|
809
|
+
const request = this.getCapabilityRequest();
|
|
810
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
811
|
+
const hasNamedKeys = request?.manifests.some((manifest) => manifest.keys?.length && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id));
|
|
812
|
+
if (hasNamedKeys) {
|
|
813
|
+
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
814
|
+
await this.refreshNodeInfo();
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/** Manifest key paths that this sign-in cannot grant. */
|
|
818
|
+
get unavailableKeyPaths() {
|
|
819
|
+
const request = this.getCapabilityRequest();
|
|
820
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
821
|
+
return (request?.manifests ?? []).flatMap(
|
|
822
|
+
(manifest) => this._nodeFeatures.includes("encryption-named-keys-v1") && appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id) ? [] : (manifest.keys ?? []).map((entry) => "path" in entry ? entry.path : entry.prefix)
|
|
823
|
+
);
|
|
824
|
+
}
|
|
769
825
|
resolveSpaceName(space, address, chainId) {
|
|
770
826
|
if (space.startsWith("tinycloud:")) {
|
|
771
827
|
return space;
|
|
@@ -957,15 +1013,32 @@ var NodeUserAuthorization = class {
|
|
|
957
1013
|
}
|
|
958
1014
|
const rawAbilities = {};
|
|
959
1015
|
const currentDid = pkhDid(address, chainId);
|
|
1016
|
+
if (this._nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
1017
|
+
const siweDomain = this.siweConfig?.domain ?? this.domain;
|
|
1018
|
+
for (const manifest of request.manifests) {
|
|
1019
|
+
if (!appKeyHostBinding(siweDomain, manifest.keys_app_id ?? manifest.app_id)) continue;
|
|
1020
|
+
for (const declaration of manifest.keys ?? []) {
|
|
1021
|
+
const path = "path" in declaration ? declaration.path : declaration.prefix;
|
|
1022
|
+
const resource = `urn:tinycloud:encryption:${currentDid}:default/${path}`;
|
|
1023
|
+
for (const ability of declaration.abilities) {
|
|
1024
|
+
addRawAbility(rawAbilities, resource, `tinycloud.encryption/${ability}`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
960
1029
|
const spaceResources = request.resources.filter((entry) => {
|
|
961
1030
|
if (entry.service !== ENCRYPTION_PERMISSION_SERVICE) {
|
|
962
1031
|
return true;
|
|
963
1032
|
}
|
|
1033
|
+
const parsed = entry.path.includes("/") ? parseEncryptionResource(entry.path) : { ...parseNetworkId(entry.path), kind: "network" };
|
|
1034
|
+
const sharePrefix = parsed.kind === "prefix" && parsed.network === "default" && parsed.prefix === "share" && entry.actions.length === 1 && entry.actions[0] === DECRYPT_ACTION && didPrincipalMatches(parsed.ownerDid, currentDid);
|
|
1035
|
+
if (parsed.kind !== "network" && !sharePrefix) {
|
|
1036
|
+
throw new ManifestValidationError("named keys go in manifest.keys");
|
|
1037
|
+
}
|
|
964
1038
|
for (const action of entry.actions) {
|
|
965
1039
|
addRawAbility(rawAbilities, entry.path, action);
|
|
966
1040
|
}
|
|
967
|
-
if (entry.actions.includes(DECRYPT_ACTION)) {
|
|
968
|
-
const parsed = parseNetworkId(entry.path);
|
|
1041
|
+
if (entry.actions.includes(DECRYPT_ACTION) && parsed.kind === "network") {
|
|
969
1042
|
if (didPrincipalMatches(parsed.ownerDid, currentDid)) {
|
|
970
1043
|
addRawAbility(rawAbilities, entry.path, NETWORK_CREATE_ACTION);
|
|
971
1044
|
}
|
|
@@ -1002,8 +1075,7 @@ var NodeUserAuthorization = class {
|
|
|
1002
1075
|
/**
|
|
1003
1076
|
* Build SIWE overrides from the top-level nonce and siweConfig.
|
|
1004
1077
|
* - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
|
|
1005
|
-
* -
|
|
1006
|
-
* - resources are appended to the default resources
|
|
1078
|
+
* - resources are inserted after WASM preparation, before the final ReCap
|
|
1007
1079
|
* - uri triggers a warning (overwriting delegation target)
|
|
1008
1080
|
* - all other fields override directly
|
|
1009
1081
|
* - per-call nonce overrides siweConfig.nonce when provided
|
|
@@ -1021,9 +1093,6 @@ var NodeUserAuthorization = class {
|
|
|
1021
1093
|
if (statement) {
|
|
1022
1094
|
overrides.statement = this.statement ? `${statement} ${this.statement}` : statement;
|
|
1023
1095
|
}
|
|
1024
|
-
if (resources && resources.length > 0) {
|
|
1025
|
-
overrides.resources = resources;
|
|
1026
|
-
}
|
|
1027
1096
|
if (uri) {
|
|
1028
1097
|
console.warn(
|
|
1029
1098
|
"[tinycloud] siweConfig.uri is overwriting the delegation target URI. This may break delegation chain validation if the URI does not match the session key DID."
|
|
@@ -1035,6 +1104,29 @@ var NodeUserAuthorization = class {
|
|
|
1035
1104
|
}
|
|
1036
1105
|
return overrides;
|
|
1037
1106
|
}
|
|
1107
|
+
prepareSessionWithResources(params) {
|
|
1108
|
+
const resources = this.siweConfig?.resources;
|
|
1109
|
+
if (resources?.some((resource) => typeof resource !== "string" || !/^urn:tinycloud:recipients:[A-Za-z0-9_-]+$/.test(resource))) {
|
|
1110
|
+
throw new Error("SIWE metadata resources must be base64url recipients URIs");
|
|
1111
|
+
}
|
|
1112
|
+
const prepared = this.wasm.prepareSession(params);
|
|
1113
|
+
if (!resources?.length) return prepared;
|
|
1114
|
+
const recapLines = /^- urn:recap:[^\r\n]+$/gim;
|
|
1115
|
+
const recaps = prepared.siwe.match(recapLines);
|
|
1116
|
+
const recap = recaps?.[0];
|
|
1117
|
+
if (recaps?.length !== 1 || !prepared.siwe.endsWith(recap)) {
|
|
1118
|
+
throw new Error("Prepared SIWE must end with exactly one ReCap");
|
|
1119
|
+
}
|
|
1120
|
+
const insertedLines = resources.map((resource) => `- ${resource}`);
|
|
1121
|
+
const siwe = prepared.siwe.slice(0, -recap.length) + insertedLines.join("\n") + "\n" + recap;
|
|
1122
|
+
const expectedFields = extractImmutableSiweFields(prepared.siwe);
|
|
1123
|
+
expectedFields.nonRecapResources = [expectedFields.nonRecapResources, ...insertedLines].filter(Boolean).join("\n");
|
|
1124
|
+
const actualRecaps = siwe.match(recapLines);
|
|
1125
|
+
if (actualRecaps?.length !== 1 || actualRecaps[0] !== recap || diffImmutableSiweFields(expectedFields, extractImmutableSiweFields(siwe)).length) {
|
|
1126
|
+
throw new Error("SIWE metadata insertion changed the prepared message");
|
|
1127
|
+
}
|
|
1128
|
+
return { ...prepared, siwe };
|
|
1129
|
+
}
|
|
1038
1130
|
/**
|
|
1039
1131
|
* Add an extension to the authorization flow.
|
|
1040
1132
|
*/
|
|
@@ -1282,7 +1374,7 @@ var NodeUserAuthorization = class {
|
|
|
1282
1374
|
spaceAbilities[spaceId] = resourceCapabilitiesToAbilitiesMap(resources);
|
|
1283
1375
|
}
|
|
1284
1376
|
const abilities = spaceAbilities[options.spaceId] ?? resourceCapabilitiesToAbilitiesMap([]);
|
|
1285
|
-
const prepared = this.
|
|
1377
|
+
const prepared = this.prepareSessionWithResources({
|
|
1286
1378
|
abilities,
|
|
1287
1379
|
spaceAbilities,
|
|
1288
1380
|
...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
|
|
@@ -1361,8 +1453,10 @@ var NodeUserAuthorization = class {
|
|
|
1361
1453
|
this._chainId = chainId;
|
|
1362
1454
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
1363
1455
|
this.assertSignInCurrent(generation);
|
|
1364
|
-
|
|
1365
|
-
this.
|
|
1456
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
1457
|
+
this.assertSignInCurrent(generation);
|
|
1458
|
+
const keyId = `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1459
|
+
this.sessionManager.createSessionKey(keyId);
|
|
1366
1460
|
this.activeSessionKeyId = keyId;
|
|
1367
1461
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
1368
1462
|
if (!jwkString) {
|
|
@@ -1373,7 +1467,7 @@ var NodeUserAuthorization = class {
|
|
|
1373
1467
|
const spaceId = capabilityPlan.spaceId;
|
|
1374
1468
|
const now = /* @__PURE__ */ new Date();
|
|
1375
1469
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
1376
|
-
const prepared = this.
|
|
1470
|
+
const prepared = this.prepareSessionWithResources({
|
|
1377
1471
|
abilities: capabilityPlan.abilities,
|
|
1378
1472
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
1379
1473
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -1507,9 +1601,9 @@ var NodeUserAuthorization = class {
|
|
|
1507
1601
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
1508
1602
|
const address = canonicalizeAddress(await this.signer.getAddress());
|
|
1509
1603
|
const chainId = await this.signer.getChainId();
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
this.
|
|
1604
|
+
await this.refreshNamedKeyFeatureForSignIn(address, chainId);
|
|
1605
|
+
const keyId = `openkey-session-${Date.now()}-${++this.preparedSessionCounter}`;
|
|
1606
|
+
this.sessionManager.createSessionKey(keyId);
|
|
1513
1607
|
const jwkString = this.sessionManager.jwk(keyId);
|
|
1514
1608
|
if (!jwkString) {
|
|
1515
1609
|
throw new Error("Failed to create session key");
|
|
@@ -1519,7 +1613,7 @@ var NodeUserAuthorization = class {
|
|
|
1519
1613
|
const spaceId = capabilityPlan.spaceId;
|
|
1520
1614
|
const now = /* @__PURE__ */ new Date();
|
|
1521
1615
|
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
1522
|
-
const prepared = this.
|
|
1616
|
+
const prepared = this.prepareSessionWithResources({
|
|
1523
1617
|
abilities: capabilityPlan.abilities,
|
|
1524
1618
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
1525
1619
|
...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
|
|
@@ -1590,6 +1684,7 @@ var NodeUserAuthorization = class {
|
|
|
1590
1684
|
await this.sessionStorage.save(address, persistedData);
|
|
1591
1685
|
this._session = clientSession;
|
|
1592
1686
|
this._tinyCloudSession = tinyCloudSession;
|
|
1687
|
+
this.activeSessionKeyId = keyId;
|
|
1593
1688
|
this._address = address;
|
|
1594
1689
|
this._chainId = chainId;
|
|
1595
1690
|
await this.refreshNodeInfo();
|
|
@@ -1636,6 +1731,11 @@ var NodeUserAuthorization = class {
|
|
|
1636
1731
|
* @param options Optional overrides for the OpenKey request.
|
|
1637
1732
|
*/
|
|
1638
1733
|
async signInWithOpenKey(authorizeFn, options) {
|
|
1734
|
+
const pending = await this.prepareOpenKeySignIn(options);
|
|
1735
|
+
return this.completeOpenKeySignIn(pending, authorizeFn(pending.request), options);
|
|
1736
|
+
}
|
|
1737
|
+
/** Finish address, host and SIWE preparation before an approval click. */
|
|
1738
|
+
async prepareOpenKeySignIn(options) {
|
|
1639
1739
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
1640
1740
|
if (options?.host !== void 0) {
|
|
1641
1741
|
const overrideHost = options.host.trim();
|
|
@@ -1647,7 +1747,7 @@ var NodeUserAuthorization = class {
|
|
|
1647
1747
|
const signingAddress = canonicalizeAddress(await this.signer.getAddress());
|
|
1648
1748
|
const signingChainId = await this.signer.getChainId();
|
|
1649
1749
|
await this.resolveTinyCloudHostsForSignIn(signingAddress, signingChainId);
|
|
1650
|
-
const { prepared, keyId, jwk } = await this.prepareSessionForSigning();
|
|
1750
|
+
const { prepared, keyId, jwk, address, chainId } = await this.prepareSessionForSigning();
|
|
1651
1751
|
const presentation = this.buildPresentationEnvelope(options?.reason);
|
|
1652
1752
|
const hostHint = this.primaryTinyCloudHost;
|
|
1653
1753
|
const originalRequest = {
|
|
@@ -1659,12 +1759,32 @@ var NodeUserAuthorization = class {
|
|
|
1659
1759
|
keyId: options?.openkeyKeyId,
|
|
1660
1760
|
presentation
|
|
1661
1761
|
};
|
|
1762
|
+
return {
|
|
1763
|
+
request: originalRequest,
|
|
1764
|
+
prepared,
|
|
1765
|
+
keyId,
|
|
1766
|
+
jwk,
|
|
1767
|
+
address,
|
|
1768
|
+
chainId,
|
|
1769
|
+
generation: this.sessionGeneration,
|
|
1770
|
+
createdAt: Date.now()
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
1774
|
+
async completeOpenKeySignIn(pending, authorization, options) {
|
|
1775
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
1776
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
1777
|
+
}
|
|
1778
|
+
if (pending.generation !== this.sessionGeneration) {
|
|
1779
|
+
throw new Error("Prepared OpenKey sign-in expired after sign-out");
|
|
1780
|
+
}
|
|
1781
|
+
const { request: originalRequest, prepared, keyId, jwk } = pending;
|
|
1662
1782
|
let result;
|
|
1663
1783
|
let signed;
|
|
1664
1784
|
let signature;
|
|
1665
1785
|
for (let attempt = 0; ; ) {
|
|
1666
1786
|
try {
|
|
1667
|
-
result ?? (result = await
|
|
1787
|
+
result ?? (result = await authorization);
|
|
1668
1788
|
signed = await this.verifyOpenKeyResult(result, prepared);
|
|
1669
1789
|
signature = result.signature;
|
|
1670
1790
|
break;
|
|
@@ -2085,6 +2205,50 @@ var NodeUserAuthorization = class {
|
|
|
2085
2205
|
}
|
|
2086
2206
|
};
|
|
2087
2207
|
|
|
2208
|
+
// src/authorization/openKeyBridge.ts
|
|
2209
|
+
function wireOpenKeyAuthorize(openkey, opts) {
|
|
2210
|
+
return async (input) => {
|
|
2211
|
+
const result = await openkey.authorizeTinyCloud({
|
|
2212
|
+
protocolVersion: 1,
|
|
2213
|
+
siwe: input.siwe,
|
|
2214
|
+
keyId: input.keyId,
|
|
2215
|
+
jwk: input.jwk,
|
|
2216
|
+
host: input.host,
|
|
2217
|
+
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
2218
|
+
// validates and size-bounds it before use.
|
|
2219
|
+
presentation: input.presentation
|
|
2220
|
+
}, opts);
|
|
2221
|
+
if (result.protocolVersion !== 1) {
|
|
2222
|
+
throw new Error(
|
|
2223
|
+
`wireOpenKeyAuthorize: OpenKey returned unsupported protocolVersion ${result.protocolVersion}`
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2226
|
+
if (typeof result.signedMessage !== "string" || !result.signedMessage) {
|
|
2227
|
+
throw new Error(
|
|
2228
|
+
"wireOpenKeyAuthorize: OpenKey returned no signedMessage \u2014 cannot complete a TinyCloud session"
|
|
2229
|
+
);
|
|
2230
|
+
}
|
|
2231
|
+
if (!Array.isArray(result.selectedActionKeys)) {
|
|
2232
|
+
throw new Error(
|
|
2233
|
+
"wireOpenKeyAuthorize: OpenKey returned no selectedActionKeys"
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
if (!Array.isArray(result.permissions)) {
|
|
2237
|
+
throw new Error(
|
|
2238
|
+
"wireOpenKeyAuthorize: OpenKey returned no permissions"
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
protocolVersion: 1,
|
|
2243
|
+
address: result.address,
|
|
2244
|
+
signature: result.signature,
|
|
2245
|
+
signedMessage: result.signedMessage,
|
|
2246
|
+
selectedActionKeys: result.selectedActionKeys,
|
|
2247
|
+
permissions: result.permissions
|
|
2248
|
+
};
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2088
2252
|
// src/recordingServiceContext.ts
|
|
2089
2253
|
import {
|
|
2090
2254
|
ServiceContext
|
|
@@ -2736,6 +2900,7 @@ function deserializeDelegation(data) {
|
|
|
2736
2900
|
|
|
2737
2901
|
// src/TinyCloudNode.ts
|
|
2738
2902
|
import { generateNonce, SiweMessage as SiweMessage3 } from "siwe";
|
|
2903
|
+
import { didKeyFromEd25519PublicKey } from "@tinycloud/share-envelope";
|
|
2739
2904
|
import {
|
|
2740
2905
|
TinyCloud,
|
|
2741
2906
|
checkNodeInfo as checkNodeInfo2,
|
|
@@ -2770,10 +2935,13 @@ import {
|
|
|
2770
2935
|
ENCRYPTION_PERMISSION_SERVICE as ENCRYPTION_PERMISSION_SERVICE2,
|
|
2771
2936
|
CaveatedDelegationUnsupportedError as CaveatedDelegationUnsupportedError2,
|
|
2772
2937
|
PermissionNotInManifestError,
|
|
2938
|
+
ManifestValidationError as ManifestValidationError2,
|
|
2773
2939
|
SessionExpiredError,
|
|
2774
2940
|
canonicalizeRecapCaveats as canonicalizeRecapCaveats2,
|
|
2775
2941
|
recapCaveatsEqual as recapCaveatsEqual2,
|
|
2776
2942
|
expandPermissionEntries as expandPermissionEntriesCore,
|
|
2943
|
+
expandRuntimePermissionEntries,
|
|
2944
|
+
expandDelegationPermissionEntries,
|
|
2777
2945
|
isCapabilitySubset as isCapabilitySubset2,
|
|
2778
2946
|
isExplicitOnlyAction,
|
|
2779
2947
|
parseRecapCapabilities,
|
|
@@ -2804,6 +2972,7 @@ import {
|
|
|
2804
2972
|
withoutShareLink,
|
|
2805
2973
|
verifyEip191MessageSignature,
|
|
2806
2974
|
signCompactUcanRootAuthorization,
|
|
2975
|
+
signCompactUcanAuthorization,
|
|
2807
2976
|
markDelegationTerminal,
|
|
2808
2977
|
isStorageFullError,
|
|
2809
2978
|
STORAGE_FULL_MESSAGE,
|
|
@@ -2819,6 +2988,7 @@ import {
|
|
|
2819
2988
|
tinyCloudDebugLogger
|
|
2820
2989
|
} from "@tinycloud/sdk-services";
|
|
2821
2990
|
import { RequestLog, observeReplicaReads, readOnlyRequestLog } from "@tinycloud/sdk-services/telemetry/requestLog";
|
|
2991
|
+
import { parseEncryptionResource as parseEncryptionResource2 } from "@tinycloud/sdk-services/encryption/resource";
|
|
2822
2992
|
|
|
2823
2993
|
// src/account/AccountService.ts
|
|
2824
2994
|
import { AccountService } from "@tinycloud/sdk-core";
|
|
@@ -3310,6 +3480,7 @@ var ROOT_DELEGATION_ACTIONS = [
|
|
|
3310
3480
|
DUCKDB2.ALL
|
|
3311
3481
|
];
|
|
3312
3482
|
var DEFAULT_SESSION_EXPIRATION_MS = EXPIRY3.SESSION_MS;
|
|
3483
|
+
var OWNER_REVOCATION_WINDOW_MS = 10 * 6e4;
|
|
3313
3484
|
function unifiedRootAttenuation(capabilities) {
|
|
3314
3485
|
const attenuation = {};
|
|
3315
3486
|
for (const capability of capabilities) {
|
|
@@ -3807,6 +3978,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3807
3978
|
constructor(config = {}) {
|
|
3808
3979
|
this.signer = null;
|
|
3809
3980
|
this.auth = null;
|
|
3981
|
+
this.consumedOpenKeyPendings = /* @__PURE__ */ new WeakSet();
|
|
3810
3982
|
this.tc = null;
|
|
3811
3983
|
this._chainId = 1;
|
|
3812
3984
|
this.replicaPurgeFailedHandlers = /* @__PURE__ */ new Set();
|
|
@@ -3841,6 +4013,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3841
4013
|
return void 0;
|
|
3842
4014
|
}
|
|
3843
4015
|
};
|
|
4016
|
+
this._keysUnavailable = [];
|
|
3844
4017
|
this._baseVaults = /* @__PURE__ */ new Map();
|
|
3845
4018
|
this._baseSecrets = /* @__PURE__ */ new Map();
|
|
3846
4019
|
this._secrets = /* @__PURE__ */ new Map();
|
|
@@ -4668,6 +4841,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4668
4841
|
this._hooks = void 0;
|
|
4669
4842
|
this._vault = void 0;
|
|
4670
4843
|
this._encryption = void 0;
|
|
4844
|
+
this._namedKeyClient = void 0;
|
|
4845
|
+
this._keysUnavailable = [];
|
|
4671
4846
|
this._baseVaults.clear();
|
|
4672
4847
|
this._baseSecrets.clear();
|
|
4673
4848
|
this._secrets.clear();
|
|
@@ -4744,6 +4919,37 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4744
4919
|
}
|
|
4745
4920
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
4746
4921
|
}
|
|
4922
|
+
/** Prepare the OpenKey request before the approval click. */
|
|
4923
|
+
async prepareOpenKeySignIn() {
|
|
4924
|
+
if (!this.signer || !this.tc || !this.auth) {
|
|
4925
|
+
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
4926
|
+
}
|
|
4927
|
+
await this.signOutInFlight;
|
|
4928
|
+
await this.accountRegistryTail;
|
|
4929
|
+
await this.wasmBindings.ensureInitialized?.();
|
|
4930
|
+
return this.auth.prepareOpenKeySignIn();
|
|
4931
|
+
}
|
|
4932
|
+
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
4933
|
+
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
4934
|
+
if (!this.signer || !this.tc || !this.auth || this.signOutInFlight) {
|
|
4935
|
+
throw new Error("Prepared OpenKey sign-in is unavailable");
|
|
4936
|
+
}
|
|
4937
|
+
if (this.consumedOpenKeyPendings.has(pending)) {
|
|
4938
|
+
throw new Error("Prepared OpenKey sign-in was already used");
|
|
4939
|
+
}
|
|
4940
|
+
if (Date.now() - pending.createdAt > 5 * 6e4) {
|
|
4941
|
+
throw new Error("Prepared OpenKey sign-in expired; prepare again");
|
|
4942
|
+
}
|
|
4943
|
+
this.consumedOpenKeyPendings.add(pending);
|
|
4944
|
+
const authorization = authorizeFn(pending.request);
|
|
4945
|
+
void authorization.catch(() => void 0);
|
|
4946
|
+
return this.signInWith(() => {
|
|
4947
|
+
if (this._address !== pending.address || this._chainId !== pending.chainId) {
|
|
4948
|
+
throw new Error("OpenKey account changed after sign-in preparation");
|
|
4949
|
+
}
|
|
4950
|
+
return this.auth.completeOpenKeySignIn(pending, authorization);
|
|
4951
|
+
});
|
|
4952
|
+
}
|
|
4747
4953
|
async signInWith(performSignIn) {
|
|
4748
4954
|
refuseCaveated(this.auth?.capabilityRequest?.manifests);
|
|
4749
4955
|
const epoch = this.sessionEpoch;
|
|
@@ -4790,6 +4996,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4790
4996
|
this._hooks = void 0;
|
|
4791
4997
|
this._vault = void 0;
|
|
4792
4998
|
this._encryption = void 0;
|
|
4999
|
+
this._namedKeyClient = void 0;
|
|
5000
|
+
this._keysUnavailable = [];
|
|
4793
5001
|
this._baseVaults.clear();
|
|
4794
5002
|
this._baseSecrets.clear();
|
|
4795
5003
|
this._secrets.clear();
|
|
@@ -4842,6 +5050,26 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4842
5050
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4843
5051
|
await this.ensureRequestedEncryptionNetworks();
|
|
4844
5052
|
this.assertSessionEpoch(epoch, "Sign-in");
|
|
5053
|
+
this._keysUnavailable = this.auth?.unavailableKeyPaths ?? [];
|
|
5054
|
+
if (this._keysUnavailable.length > 0) {
|
|
5055
|
+
this.notificationHandler.warning(`Named keys unavailable: ${this._keysUnavailable.join(", ")}`);
|
|
5056
|
+
}
|
|
5057
|
+
if (this.nodeFeatures.includes("encryption-named-keys-v1")) {
|
|
5058
|
+
for (const manifest of this.auth?.capabilityRequest?.manifests ?? []) {
|
|
5059
|
+
for (const declaration of manifest.keys ?? []) {
|
|
5060
|
+
if ("path" in declaration && declaration.abilities.includes("key.create") && !this._keysUnavailable.includes(declaration.path)) {
|
|
5061
|
+
try {
|
|
5062
|
+
await this.keys.ensureKey(declaration.path);
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
this._keysUnavailable.push(declaration.path);
|
|
5065
|
+
const code = error && typeof error === "object" && "code" in error && typeof error.code === "string" && /^[A-Z_]+$/.test(error.code) ? error.code : "UNKNOWN";
|
|
5066
|
+
this.notificationHandler.warning(`Named key unavailable: ${declaration.path} (${code})`);
|
|
5067
|
+
}
|
|
5068
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
4845
5073
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
4846
5074
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(SECRETS_SPACE2));
|
|
4847
5075
|
}
|
|
@@ -5629,7 +5857,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5629
5857
|
const networkIds = /* @__PURE__ */ new Set();
|
|
5630
5858
|
for (const resource of request.resources) {
|
|
5631
5859
|
if (resource.service === ENCRYPTION_PERMISSION_SERVICE2 && resource.path.startsWith("urn:tinycloud:encryption:") && resource.actions.includes(DECRYPT_ACTION2)) {
|
|
5632
|
-
|
|
5860
|
+
const parsed = resource.path.includes("/") ? parseEncryptionResource2(resource.path) : { ...parseNetworkId2(resource.path), kind: "network" };
|
|
5861
|
+
if (parsed.kind === "network") networkIds.add(resource.path);
|
|
5633
5862
|
}
|
|
5634
5863
|
}
|
|
5635
5864
|
return [...networkIds];
|
|
@@ -6179,6 +6408,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6179
6408
|
this._hooks = stagedGraph.hooks;
|
|
6180
6409
|
this._vault = stagedGraph.vault;
|
|
6181
6410
|
this._encryption = stagedGraph.encryption;
|
|
6411
|
+
this._namedKeyClient = void 0;
|
|
6412
|
+
this._keysUnavailable = [];
|
|
6182
6413
|
this._capabilityRegistry = stagedGraph.capabilityRegistry;
|
|
6183
6414
|
this._keyProvider = stagedGraph.keyProvider;
|
|
6184
6415
|
this._sharingService = stagedGraph.sharingService;
|
|
@@ -6360,6 +6591,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6360
6591
|
invoke: graph.invoke,
|
|
6361
6592
|
invokeAny: graph.invokeAny,
|
|
6362
6593
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
6594
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(input.serviceSession),
|
|
6363
6595
|
fetch: graph.fetch
|
|
6364
6596
|
});
|
|
6365
6597
|
const keyProvider = new WasmKeyProvider({ sessionManager: input.manager });
|
|
@@ -6575,6 +6807,31 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
6575
6807
|
if (session === void 0) throw new Error("Not signed in. Call signIn() first.");
|
|
6576
6808
|
return signBytesWithJwk(bytes, session.jwk);
|
|
6577
6809
|
}
|
|
6810
|
+
/**
|
|
6811
|
+
* Signs proofless invocations with `session`'s own did:key, for the node's
|
|
6812
|
+
* issued-delegation list and revocation by the delegator. The audience is
|
|
6813
|
+
* this Node's DID; each invocation lives 60 seconds.
|
|
6814
|
+
*/
|
|
6815
|
+
sessionInvocationSignerFor(session) {
|
|
6816
|
+
return {
|
|
6817
|
+
did: () => session.verificationMethod.split("#", 1)[0],
|
|
6818
|
+
sign: async (attenuation) => {
|
|
6819
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
6820
|
+
const invocation = await signCompactUcanAuthorization({
|
|
6821
|
+
issuerDid: session.verificationMethod.split("#", 1)[0],
|
|
6822
|
+
audienceDid: await this.fetchNodeId(),
|
|
6823
|
+
attenuation,
|
|
6824
|
+
facts: [{}],
|
|
6825
|
+
proofs: [],
|
|
6826
|
+
notBefore: now,
|
|
6827
|
+
expiresAt: now + 60,
|
|
6828
|
+
nonce: `urn:uuid:${crypto.randomUUID()}`,
|
|
6829
|
+
sign: (bytes) => signBytesWithJwk(bytes, session.jwk)
|
|
6830
|
+
});
|
|
6831
|
+
return invocation.authorization;
|
|
6832
|
+
}
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6578
6835
|
/** Author a proofless Policy/v3 owner root with the authenticated holder key. */
|
|
6579
6836
|
async createUnifiedOwnerRoot(input) {
|
|
6580
6837
|
const ownerDid = this.credentialHolderDid;
|
|
@@ -7109,7 +7366,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7109
7366
|
graph.assertActive();
|
|
7110
7367
|
const signed = await this.signRawNetworkAuthorization({
|
|
7111
7368
|
targetNode: input.targetNode,
|
|
7112
|
-
networkId: input.networkId
|
|
7369
|
+
networkId: input.body.keyPath === void 0 ? input.networkId : `${input.networkId}/${input.body.keyPath}`,
|
|
7113
7370
|
action: DECRYPT_ACTION2,
|
|
7114
7371
|
facts: input.facts
|
|
7115
7372
|
}, { graph, session });
|
|
@@ -7282,6 +7539,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7282
7539
|
invoke: graph.invoke,
|
|
7283
7540
|
invokeAny: graph.invokeAny,
|
|
7284
7541
|
signOwnerRevocation: this.signer ? (cid) => this.signOwnerRevocation(cid) : void 0,
|
|
7542
|
+
sessionInvocationSigner: this.sessionInvocationSignerFor(serviceSession),
|
|
7285
7543
|
fetch: graph.fetch
|
|
7286
7544
|
});
|
|
7287
7545
|
this._spaceService = new SpaceService({
|
|
@@ -7532,7 +7790,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7532
7790
|
* Sign a wallet (CACAO) revocation of the delegation `cid` for the node's
|
|
7533
7791
|
* `POST /revoke`, and return its Authorization header value. The node
|
|
7534
7792
|
* accepts it when the owner delegated `cid` or owns a space its abilities
|
|
7535
|
-
* target, whichever key issued it. Valid for
|
|
7793
|
+
* target, whichever key issued it. Valid for 10 minutes. Requires the
|
|
7536
7794
|
* owner wallet signer.
|
|
7537
7795
|
*/
|
|
7538
7796
|
async signOwnerRevocation(cid) {
|
|
@@ -7548,7 +7806,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7548
7806
|
chainId: session.chainId,
|
|
7549
7807
|
nonce: generateNonce(),
|
|
7550
7808
|
issuedAt: issuedAt.toISOString(),
|
|
7551
|
-
expirationTime: new Date(issuedAt.getTime() +
|
|
7809
|
+
expirationTime: new Date(issuedAt.getTime() + OWNER_REVOCATION_WINDOW_MS).toISOString()
|
|
7552
7810
|
}).prepareMessage();
|
|
7553
7811
|
const signature = await this.signer.signMessage(siwe);
|
|
7554
7812
|
return this.wasmBindings.siweToDelegationHeaders({ siwe, signature }).Authorization;
|
|
@@ -7775,6 +8033,40 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7775
8033
|
get encryption() {
|
|
7776
8034
|
return this.getEncryptionService();
|
|
7777
8035
|
}
|
|
8036
|
+
/** Named keys bound to the current session. */
|
|
8037
|
+
get keys() {
|
|
8038
|
+
return new Proxy({}, {
|
|
8039
|
+
get: (_target, property) => {
|
|
8040
|
+
if (property === "refreshNodeInfo") {
|
|
8041
|
+
return () => {
|
|
8042
|
+
void this._namedKeyClient?.then((client) => client.refreshNodeInfo());
|
|
8043
|
+
};
|
|
8044
|
+
}
|
|
8045
|
+
if (typeof property !== "string" || ![
|
|
8046
|
+
"getPublicKey",
|
|
8047
|
+
"describeKey",
|
|
8048
|
+
"createKey",
|
|
8049
|
+
"blockKey",
|
|
8050
|
+
"unblockKey",
|
|
8051
|
+
"ensureKey",
|
|
8052
|
+
"encrypt",
|
|
8053
|
+
"decrypt",
|
|
8054
|
+
"decryptMany"
|
|
8055
|
+
].includes(property)) return void 0;
|
|
8056
|
+
return (...args) => {
|
|
8057
|
+
this._namedKeyClient ?? (this._namedKeyClient = import("./encryption-keys.js").then(({ createNamedKeyClient }) => createNamedKeyClient(this)));
|
|
8058
|
+
return this._namedKeyClient.then((client) => {
|
|
8059
|
+
const method = client[property];
|
|
8060
|
+
return method.apply(client, args);
|
|
8061
|
+
});
|
|
8062
|
+
};
|
|
8063
|
+
}
|
|
8064
|
+
});
|
|
8065
|
+
}
|
|
8066
|
+
/** Manifest key paths unavailable during the latest `signIn()` attempt. */
|
|
8067
|
+
get keysUnavailable() {
|
|
8068
|
+
return this._keysUnavailable;
|
|
8069
|
+
}
|
|
7778
8070
|
async getEncryptionNetwork(nameOrNetworkId = this.getDefaultEncryptionNetworkId()) {
|
|
7779
8071
|
const networkId = nameOrNetworkId.startsWith("urn:tinycloud:encryption:") ? nameOrNetworkId : this.getDefaultEncryptionNetworkId(nameOrNetworkId);
|
|
7780
8072
|
return this.fetchEncryptionNetworkAt(
|
|
@@ -8031,7 +8323,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8031
8323
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
8032
8324
|
return false;
|
|
8033
8325
|
}
|
|
8034
|
-
const expanded =
|
|
8326
|
+
const expanded = expandRuntimePermissionEntries(permissions, pkhDid2(session.address, session.chainId));
|
|
8035
8327
|
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
8036
8328
|
return true;
|
|
8037
8329
|
}
|
|
@@ -8051,7 +8343,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8051
8343
|
if (!session || !Array.isArray(permissions) || permissions.length === 0) {
|
|
8052
8344
|
return [];
|
|
8053
8345
|
}
|
|
8054
|
-
const expanded =
|
|
8346
|
+
const expanded = expandRuntimePermissionEntries(permissions, pkhDid2(session.address, session.chainId));
|
|
8055
8347
|
return this.findRuntimeGrantsForPermissionEntries(expanded, session).map(
|
|
8056
8348
|
(grant) => grant.delegation
|
|
8057
8349
|
);
|
|
@@ -8226,7 +8518,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8226
8518
|
throw new SessionExpiredError(sessionExpiry);
|
|
8227
8519
|
}
|
|
8228
8520
|
}
|
|
8229
|
-
const expanded =
|
|
8521
|
+
const expanded = expandRuntimePermissionEntries(permissions, pkhDid2(session.address, session.chainId));
|
|
8522
|
+
const bindsShareKey = expanded.some((entry) => entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.actions.includes("tinycloud.encryption/key.create"));
|
|
8523
|
+
const captured = bindsShareKey ? { ...session, jwk: { ...options?.expectedSessionKey ?? session.jwk } } : session;
|
|
8524
|
+
const assertCapturedKey = bindsShareKey ? () => {
|
|
8525
|
+
const live = session.jwk;
|
|
8526
|
+
const key = captured.jwk;
|
|
8527
|
+
if (this.currentTinyCloudSession() !== session || session.verificationMethod !== captured.verificationMethod || session.delegationCid !== captured.delegationCid || session.spaceId !== captured.spaceId || session.address !== captured.address || session.chainId !== captured.chainId || live?.kty !== key.kty || live?.crv !== key.crv || live?.x !== key.x || live?.y !== key.y || live?.d !== key.d) {
|
|
8528
|
+
throw new Error("Runtime permission request cancelled because the session key changed.");
|
|
8529
|
+
}
|
|
8530
|
+
} : void 0;
|
|
8531
|
+
const sessionKeyDid = bindsShareKey ? this.sessionDid : void 0;
|
|
8532
|
+
if (bindsShareKey) {
|
|
8533
|
+
const key = captured.jwk;
|
|
8534
|
+
if (key.kty !== "OKP" || key.crv !== "Ed25519" || typeof key.x !== "string") {
|
|
8535
|
+
throw new Error("Runtime permission session key does not match its signed identity.");
|
|
8536
|
+
}
|
|
8537
|
+
const keyDid = didKeyFromEd25519PublicKey(base64UrlDecode(key.x));
|
|
8538
|
+
if (!didPrincipalMatches2(keyDid, sessionKeyDid) || !didPrincipalMatches2(keyDid, captured.verificationMethod) || !didPrincipalMatches2(keyDid, new SiweMessage3(session.siwe).uri)) {
|
|
8539
|
+
throw new Error("Runtime permission session key does not match its signed identity.");
|
|
8540
|
+
}
|
|
8541
|
+
assertCapturedKey();
|
|
8542
|
+
}
|
|
8230
8543
|
if (this.sessionCoversPermissionEntries(session, expanded)) {
|
|
8231
8544
|
return [];
|
|
8232
8545
|
}
|
|
@@ -8276,13 +8589,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8276
8589
|
return this.wasmBindings.prepareSession({
|
|
8277
8590
|
abilities,
|
|
8278
8591
|
...rawForDelegation.length > 0 ? { rawAbilities: this.permissionsToRawAbilities(rawForDelegation) } : {},
|
|
8279
|
-
address: this.wasmBindings.ensureEip55(
|
|
8280
|
-
chainId:
|
|
8592
|
+
address: this.wasmBindings.ensureEip55(captured.address),
|
|
8593
|
+
chainId: captured.chainId,
|
|
8281
8594
|
domain: this.siweDomain,
|
|
8282
8595
|
issuedAt: now.toISOString(),
|
|
8283
8596
|
expirationTime: expiresAt.toISOString(),
|
|
8284
8597
|
spaceId,
|
|
8285
|
-
jwk:
|
|
8598
|
+
jwk: captured.jwk
|
|
8286
8599
|
});
|
|
8287
8600
|
} catch (error) {
|
|
8288
8601
|
if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && /invalid ReCap resource URI/i.test(error instanceof Error ? error.message : String(error))) {
|
|
@@ -8294,8 +8607,13 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8294
8607
|
throw error;
|
|
8295
8608
|
}
|
|
8296
8609
|
})();
|
|
8610
|
+
if (bindsShareKey && !didPrincipalMatches2(new SiweMessage3(prepared.siwe).uri, sessionKeyDid)) {
|
|
8611
|
+
throw new Error("Prepared runtime permission targets a different session key.");
|
|
8612
|
+
}
|
|
8613
|
+
assertCapturedKey?.();
|
|
8297
8614
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
8298
8615
|
graph.assertActive();
|
|
8616
|
+
assertCapturedKey?.();
|
|
8299
8617
|
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
8300
8618
|
...prepared,
|
|
8301
8619
|
signature
|
|
@@ -8324,18 +8642,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8324
8642
|
delegatedSession,
|
|
8325
8643
|
delegatedEntries,
|
|
8326
8644
|
spaceId,
|
|
8327
|
-
|
|
8645
|
+
captured,
|
|
8328
8646
|
expiresAt,
|
|
8329
8647
|
{ siwe: prepared.siwe, signature }
|
|
8330
8648
|
);
|
|
8331
8649
|
graph.assertActive();
|
|
8650
|
+
assertCapturedKey?.();
|
|
8332
8651
|
this.runtimePermissionGrants.push({
|
|
8333
8652
|
session: {
|
|
8334
8653
|
delegationHeader: delegatedSession.delegationHeader,
|
|
8335
8654
|
delegationCid: delegatedSession.delegationCid,
|
|
8336
8655
|
spaceId,
|
|
8337
|
-
verificationMethod:
|
|
8338
|
-
jwk:
|
|
8656
|
+
verificationMethod: captured.verificationMethod,
|
|
8657
|
+
jwk: captured.jwk
|
|
8339
8658
|
},
|
|
8340
8659
|
delegation,
|
|
8341
8660
|
operations: this.permissionOperations(delegatedEntries, spaceId),
|
|
@@ -8664,7 +8983,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8664
8983
|
"delegateTo requires a non-empty permissions array"
|
|
8665
8984
|
);
|
|
8666
8985
|
}
|
|
8667
|
-
const expandedEntries =
|
|
8986
|
+
const expandedEntries = expandDelegationPermissionEntries(permissions);
|
|
8987
|
+
const namedKeyEntries = expandedEntries.filter((entry) => entry.service === ENCRYPTION_PERMISSION_SERVICE2 && entry.path.includes("/"));
|
|
8988
|
+
if (namedKeyEntries.length > 0) {
|
|
8989
|
+
if (expandedEntries.some((entry) => entry.service === ENCRYPTION_PERMISSION_SERVICE2 && !entry.path.includes("/"))) {
|
|
8990
|
+
throw new ManifestValidationError2("named-key delegation cannot include bare-network permissions");
|
|
8991
|
+
}
|
|
8992
|
+
if (options?.forceWalletSign) {
|
|
8993
|
+
throw new ManifestValidationError2("named-key delegation requires a signed session grant");
|
|
8994
|
+
}
|
|
8995
|
+
const granted = this.projectSignedRecapCapabilities(session.siwe);
|
|
8996
|
+
const { subset, missing } = this.signedCapabilitySubset(namedKeyEntries, granted);
|
|
8997
|
+
if (!subset) throw new PermissionNotInManifestError(missing, granted);
|
|
8998
|
+
}
|
|
8668
8999
|
const now = /* @__PURE__ */ new Date();
|
|
8669
9000
|
const expiryMs = resolveExpiryMs(options?.expiry);
|
|
8670
9001
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
@@ -10049,7 +10380,7 @@ import {
|
|
|
10049
10380
|
CaveatedDelegationUnsupportedError as CaveatedDelegationUnsupportedError3,
|
|
10050
10381
|
PermissionNotInManifestError as PermissionNotInManifestError2,
|
|
10051
10382
|
SessionExpiredError as SessionExpiredError2,
|
|
10052
|
-
ManifestValidationError,
|
|
10383
|
+
ManifestValidationError as ManifestValidationError3,
|
|
10053
10384
|
composeManifestRequest as composeManifestRequest2,
|
|
10054
10385
|
resolveManifest as resolveManifest2,
|
|
10055
10386
|
validateManifest,
|
|
@@ -10134,12 +10465,13 @@ export {
|
|
|
10134
10465
|
IdentityParseError,
|
|
10135
10466
|
InvalidRestoredSessionError,
|
|
10136
10467
|
KVService3 as KVService,
|
|
10137
|
-
ManifestValidationError,
|
|
10468
|
+
ManifestValidationError3 as ManifestValidationError,
|
|
10138
10469
|
MemorySessionStorage,
|
|
10139
10470
|
NodeUserAuthorization,
|
|
10140
10471
|
PermissionNotInManifestError2 as PermissionNotInManifestError,
|
|
10141
10472
|
PrefixedKVService,
|
|
10142
10473
|
ProtocolMismatchError2 as ProtocolMismatchError,
|
|
10474
|
+
RuntimeDelegationRejectedError,
|
|
10143
10475
|
SECRET_NAME_RE,
|
|
10144
10476
|
SQLAction,
|
|
10145
10477
|
SQLService3 as SQLService,
|
|
@@ -10162,6 +10494,7 @@ export {
|
|
|
10162
10494
|
VersionCheckError,
|
|
10163
10495
|
WasmKeyProvider,
|
|
10164
10496
|
activateCompactRuntimeDelegation,
|
|
10497
|
+
activateValidatedRuntimeDelegation,
|
|
10165
10498
|
addressStorageKey,
|
|
10166
10499
|
buildSpaceUri,
|
|
10167
10500
|
canonicalizeAddress3 as canonicalizeAddress,
|
|
@@ -10191,6 +10524,7 @@ export {
|
|
|
10191
10524
|
getNodeReplicationLoaders,
|
|
10192
10525
|
getTinyCloudDebugLogs,
|
|
10193
10526
|
installTinyCloudDebugGlobals,
|
|
10527
|
+
isAppKeyHostBound,
|
|
10194
10528
|
isCapabilitySubset3 as isCapabilitySubset,
|
|
10195
10529
|
isEvmAddress,
|
|
10196
10530
|
loadManifest,
|
|
@@ -10212,6 +10546,7 @@ export {
|
|
|
10212
10546
|
resourceCapabilitiesToSpaceAbilitiesMap2 as resourceCapabilitiesToSpaceAbilitiesMap,
|
|
10213
10547
|
serializeDelegation,
|
|
10214
10548
|
tinyCloudDebugLogger2 as tinyCloudDebugLogger,
|
|
10215
|
-
validateManifest
|
|
10549
|
+
validateManifest,
|
|
10550
|
+
wireOpenKeyAuthorize
|
|
10216
10551
|
};
|
|
10217
10552
|
//# sourceMappingURL=core.js.map
|