@tinycloud/sdk-core 3.0.0-beta.0 → 3.0.0-beta.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/index.cjs +238 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -136,6 +136,7 @@ __export(index_exports, {
|
|
|
136
136
|
NETWORK_NAME_PATTERN: () => import_sdk_services8.NETWORK_NAME_PATTERN,
|
|
137
137
|
NetworkIdError: () => import_sdk_services8.NetworkIdError,
|
|
138
138
|
OPENKEY_ACTION_ID_SEPARATOR: () => OPENKEY_ACTION_ID_SEPARATOR,
|
|
139
|
+
OpenKeyManageKeyError: () => OpenKeyManageKeyError,
|
|
139
140
|
POLICY_CAPABILITY_V1_DOMAIN: () => POLICY_CAPABILITY_V1_DOMAIN,
|
|
140
141
|
POLICY_CAPABILITY_V1_SCHEMA: () => POLICY_CAPABILITY_V1_SCHEMA,
|
|
141
142
|
POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE: () => POLICY_CREDENTIAL_REQUIREMENT_V1_TYPE,
|
|
@@ -206,7 +207,9 @@ __export(index_exports, {
|
|
|
206
207
|
SpaceService: () => SpaceService,
|
|
207
208
|
TINYCLOUD_ACCOUNT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
|
|
208
209
|
TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
|
|
210
|
+
TINYCLOUD_CANONICAL_IDENTITY_CLAIM: () => TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
|
|
209
211
|
TINYCLOUD_DEFAULT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_DEFAULT_SPACE_MANIFEST,
|
|
212
|
+
TINYCLOUD_MANAGE_KEY_SCOPE: () => TINYCLOUD_MANAGE_KEY_SCOPE,
|
|
210
213
|
TINYCLOUD_PUBLIC_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_PUBLIC_SPACE_MANIFEST,
|
|
211
214
|
TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST: () => import_bootstrap4.TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,
|
|
212
215
|
TinyCloud: () => TinyCloud,
|
|
@@ -267,6 +270,7 @@ __export(index_exports, {
|
|
|
267
270
|
createInMemoryLocalNodeIdentityStore: () => createInMemoryLocalNodeIdentityStore,
|
|
268
271
|
createLocalStorageLocalNodeIdentityStore: () => createLocalStorageLocalNodeIdentityStore,
|
|
269
272
|
createOpenKeyCallbackSigningStrategy: () => createOpenKeyCallbackSigningStrategy,
|
|
273
|
+
createOpenKeyManageKeySigningStrategy: () => createOpenKeyManageKeySigningStrategy,
|
|
270
274
|
createPolicyEnforcementDelegation: () => createPolicyEnforcementDelegation,
|
|
271
275
|
createShareArtifact: () => createShareArtifact,
|
|
272
276
|
createShareArtifactAsync: () => createShareArtifactAsync,
|
|
@@ -325,6 +329,7 @@ __export(index_exports, {
|
|
|
325
329
|
generateRandomReceiverKey: () => import_sdk_services8.generateRandomReceiverKey,
|
|
326
330
|
getPolicyRootStatusV3: () => getPolicyRootStatusV3,
|
|
327
331
|
getTinyCloudDebugLogs: () => import_sdk_services8.getTinyCloudDebugLogs,
|
|
332
|
+
hasTinyCloudManageKeyScope: () => hasTinyCloudManageKeyScope,
|
|
328
333
|
hexDecode: () => import_sdk_services8.hexDecode,
|
|
329
334
|
hexEncode: () => import_sdk_services8.hexEncode,
|
|
330
335
|
holderBindingCanonicalBytes: () => holderBindingCanonicalBytes,
|
|
@@ -357,6 +362,8 @@ __export(index_exports, {
|
|
|
357
362
|
openWrappedKey: () => import_sdk_services8.openWrappedKey,
|
|
358
363
|
parseCanonicalNetworkId: () => parseCanonicalNetworkId,
|
|
359
364
|
parseCanonicalRecapResource: () => parseCanonicalRecapResource,
|
|
365
|
+
parseCanonicalTinyCloudIdentity: () => parseCanonicalTinyCloudIdentity,
|
|
366
|
+
parseCanonicalTinyCloudIdentityClaims: () => parseCanonicalTinyCloudIdentityClaims,
|
|
360
367
|
parseCompactUcanAuthorization: () => parseCompactUcanAuthorization,
|
|
361
368
|
parseExpiry: () => parseExpiry,
|
|
362
369
|
parseNetworkId: () => import_sdk_services8.parseNetworkId,
|
|
@@ -379,6 +386,7 @@ __export(index_exports, {
|
|
|
379
386
|
projectUnifiedPolicyCapability: () => projectUnifiedPolicyCapability,
|
|
380
387
|
recapCaveatsEqual: () => recapCaveatsEqual,
|
|
381
388
|
renewPolicyRootStatusV3: () => renewPolicyRootStatusV3,
|
|
389
|
+
requestTinyCloudManageKeyScope: () => requestTinyCloudManageKeyScope,
|
|
382
390
|
resolveCloudLocation: () => resolveCloudLocation,
|
|
383
391
|
resolveManifest: () => resolveManifest,
|
|
384
392
|
resolveManifestKnowledgeRoot: () => resolveManifestKnowledgeRoot,
|
|
@@ -15367,6 +15375,236 @@ function createOpenKeyCallbackSigningStrategy(options2) {
|
|
|
15367
15375
|
}
|
|
15368
15376
|
var defaultSignStrategy = { type: "auto-sign" };
|
|
15369
15377
|
|
|
15378
|
+
// src/authorization/manage-key.ts
|
|
15379
|
+
var TINYCLOUD_MANAGE_KEY_SCOPE = "tinycloud:manage-key";
|
|
15380
|
+
var TINYCLOUD_CANONICAL_IDENTITY_CLAIM = "https://tinycloud.xyz/canonical_identity";
|
|
15381
|
+
function requestTinyCloudManageKeyScope(scopes = []) {
|
|
15382
|
+
const requested = typeof scopes === "string" ? scopes.split(/\s+/u) : scopes;
|
|
15383
|
+
return [
|
|
15384
|
+
...new Set([...requested, TINYCLOUD_MANAGE_KEY_SCOPE].filter(Boolean))
|
|
15385
|
+
].sort().join(" ");
|
|
15386
|
+
}
|
|
15387
|
+
function hasTinyCloudManageKeyScope(scopes) {
|
|
15388
|
+
const values = Array.isArray(scopes) ? scopes : typeof scopes === "string" ? scopes.split(/\s+/u) : [];
|
|
15389
|
+
return values.includes(TINYCLOUD_MANAGE_KEY_SCOPE);
|
|
15390
|
+
}
|
|
15391
|
+
var OpenKeyManageKeyError = class extends Error {
|
|
15392
|
+
constructor(code3, message, approvalUrl, retryable = false) {
|
|
15393
|
+
super(message);
|
|
15394
|
+
this.code = code3;
|
|
15395
|
+
this.approvalUrl = approvalUrl;
|
|
15396
|
+
this.retryable = retryable;
|
|
15397
|
+
this.name = "OpenKeyManageKeyError";
|
|
15398
|
+
}
|
|
15399
|
+
};
|
|
15400
|
+
function isRecord2(value) {
|
|
15401
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15402
|
+
}
|
|
15403
|
+
function rejected(message) {
|
|
15404
|
+
return new OpenKeyManageKeyError("MESSAGE_REJECTED", message);
|
|
15405
|
+
}
|
|
15406
|
+
function identityMismatch(message) {
|
|
15407
|
+
return new OpenKeyManageKeyError("IDENTITY_MISMATCH", message);
|
|
15408
|
+
}
|
|
15409
|
+
function parseCanonicalTinyCloudIdentity(value) {
|
|
15410
|
+
if (!isRecord2(value))
|
|
15411
|
+
throw rejected("Canonical TinyCloud identity is missing");
|
|
15412
|
+
const expectedKeys = [
|
|
15413
|
+
"address",
|
|
15414
|
+
"chainId",
|
|
15415
|
+
"did",
|
|
15416
|
+
"keyId",
|
|
15417
|
+
"spaceId",
|
|
15418
|
+
"version"
|
|
15419
|
+
];
|
|
15420
|
+
const actualKeys = Object.keys(value).sort();
|
|
15421
|
+
if (actualKeys.length !== expectedKeys.length || actualKeys.some((key, index) => key !== expectedKeys[index])) {
|
|
15422
|
+
throw rejected("Canonical TinyCloud identity has an invalid shape");
|
|
15423
|
+
}
|
|
15424
|
+
if (value.version !== "v1")
|
|
15425
|
+
throw rejected("Canonical TinyCloud identity version is unsupported");
|
|
15426
|
+
if (typeof value.keyId !== "string" || value.keyId.trim() === "") {
|
|
15427
|
+
throw rejected("Canonical TinyCloud identity key ID is invalid");
|
|
15428
|
+
}
|
|
15429
|
+
if (typeof value.address !== "string") {
|
|
15430
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15431
|
+
}
|
|
15432
|
+
let address;
|
|
15433
|
+
try {
|
|
15434
|
+
address = canonicalizeAddress(value.address);
|
|
15435
|
+
} catch {
|
|
15436
|
+
throw rejected("Canonical TinyCloud identity address is invalid");
|
|
15437
|
+
}
|
|
15438
|
+
if (address !== value.address) {
|
|
15439
|
+
throw rejected(
|
|
15440
|
+
"Canonical TinyCloud identity address must use EIP-55 checksum casing"
|
|
15441
|
+
);
|
|
15442
|
+
}
|
|
15443
|
+
if (typeof value.chainId !== "number" || !Number.isSafeInteger(value.chainId) || value.chainId <= 0) {
|
|
15444
|
+
throw rejected("Canonical TinyCloud identity chain ID is invalid");
|
|
15445
|
+
}
|
|
15446
|
+
const did = pkhDid(address, value.chainId);
|
|
15447
|
+
const spacePrefix = `tinycloud:pkh:eip155:${value.chainId}:${address}:`;
|
|
15448
|
+
if (typeof value.spaceId !== "string" || !value.spaceId.startsWith(spacePrefix)) {
|
|
15449
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15450
|
+
}
|
|
15451
|
+
const spaceName = value.spaceId.slice(spacePrefix.length);
|
|
15452
|
+
let spaceId;
|
|
15453
|
+
try {
|
|
15454
|
+
spaceId = makePkhSpaceId(address, value.chainId, spaceName);
|
|
15455
|
+
} catch {
|
|
15456
|
+
throw rejected("Canonical TinyCloud identity space is invalid");
|
|
15457
|
+
}
|
|
15458
|
+
if (value.did !== did || value.spaceId !== spaceId) {
|
|
15459
|
+
throw rejected("Canonical TinyCloud identity does not match its address");
|
|
15460
|
+
}
|
|
15461
|
+
return {
|
|
15462
|
+
version: "v1",
|
|
15463
|
+
keyId: value.keyId,
|
|
15464
|
+
address,
|
|
15465
|
+
chainId: value.chainId,
|
|
15466
|
+
did,
|
|
15467
|
+
spaceId
|
|
15468
|
+
};
|
|
15469
|
+
}
|
|
15470
|
+
function parseCanonicalTinyCloudIdentityClaims(claims) {
|
|
15471
|
+
if (!isRecord2(claims)) throw rejected("OAuth claims are missing");
|
|
15472
|
+
return parseCanonicalTinyCloudIdentity(
|
|
15473
|
+
claims[TINYCLOUD_CANONICAL_IDENTITY_CLAIM]
|
|
15474
|
+
);
|
|
15475
|
+
}
|
|
15476
|
+
function identitiesEqual(left, right) {
|
|
15477
|
+
return left.version === right.version && left.keyId === right.keyId && left.address === right.address && left.chainId === right.chainId && left.did === right.did && left.spaceId === right.spaceId;
|
|
15478
|
+
}
|
|
15479
|
+
function errorForResponse(body, status) {
|
|
15480
|
+
const message = body?.reason ?? body?.error ?? `OpenKey manage-key signing failed with HTTP ${status}`;
|
|
15481
|
+
switch (body?.code ?? body?.error) {
|
|
15482
|
+
case "missing_scope":
|
|
15483
|
+
case "consent_required":
|
|
15484
|
+
return new OpenKeyManageKeyError("CONSENT_REQUIRED", message, body?.approvalUrl);
|
|
15485
|
+
case "client_disabled":
|
|
15486
|
+
case "signing_disabled":
|
|
15487
|
+
case "grant_disabled":
|
|
15488
|
+
return new OpenKeyManageKeyError("GRANT_DISABLED", message, body?.approvalUrl);
|
|
15489
|
+
case "user_exclusive":
|
|
15490
|
+
return new OpenKeyManageKeyError("USER_EXCLUSIVE", message, body?.approvalUrl);
|
|
15491
|
+
case "token_expired":
|
|
15492
|
+
case "token_too_old":
|
|
15493
|
+
case "invalid_token":
|
|
15494
|
+
case "expired_token":
|
|
15495
|
+
return new OpenKeyManageKeyError("TOKEN_EXPIRED", message, body?.approvalUrl);
|
|
15496
|
+
case "canonical_key_unavailable":
|
|
15497
|
+
case "signer_failed":
|
|
15498
|
+
return new OpenKeyManageKeyError(
|
|
15499
|
+
"SIGNER_UNAVAILABLE",
|
|
15500
|
+
"The canonical key signer is temporarily unavailable",
|
|
15501
|
+
void 0,
|
|
15502
|
+
true
|
|
15503
|
+
);
|
|
15504
|
+
default:
|
|
15505
|
+
return new OpenKeyManageKeyError(
|
|
15506
|
+
status === 401 ? "TOKEN_EXPIRED" : "MESSAGE_REJECTED",
|
|
15507
|
+
message,
|
|
15508
|
+
body?.approvalUrl
|
|
15509
|
+
);
|
|
15510
|
+
}
|
|
15511
|
+
}
|
|
15512
|
+
async function resolveToken(token) {
|
|
15513
|
+
const value = typeof token === "function" ? await token() : token;
|
|
15514
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
15515
|
+
throw new OpenKeyManageKeyError(
|
|
15516
|
+
"CONSENT_REQUIRED",
|
|
15517
|
+
"A manage-key OAuth bearer token is required"
|
|
15518
|
+
);
|
|
15519
|
+
}
|
|
15520
|
+
return value;
|
|
15521
|
+
}
|
|
15522
|
+
function normalizeEndpoint(value) {
|
|
15523
|
+
let endpoint;
|
|
15524
|
+
try {
|
|
15525
|
+
endpoint = new URL(value);
|
|
15526
|
+
} catch {
|
|
15527
|
+
throw rejected("OpenKey manage-key signing endpoint must be an absolute URL");
|
|
15528
|
+
}
|
|
15529
|
+
const rawHttpAuthority = value.match(/^http:\/\/([^/?#]*)/iu)?.[1];
|
|
15530
|
+
const loopback = rawHttpAuthority !== void 0 && /^(?:localhost|127\.0\.0\.1|\[::1\])(?::[0-9]+)?$/u.test(rawHttpAuthority);
|
|
15531
|
+
if (endpoint.protocol !== "https:" && !(endpoint.protocol === "http:" && loopback) || endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "") {
|
|
15532
|
+
throw rejected("OpenKey manage-key signing endpoint must use HTTPS or exact loopback HTTP");
|
|
15533
|
+
}
|
|
15534
|
+
return endpoint.href;
|
|
15535
|
+
}
|
|
15536
|
+
function assertSignInRequest(request, identity3) {
|
|
15537
|
+
if (request.type !== "siwe" || request.purpose !== "sign-in" || request.chainId !== identity3.chainId || request.address !== identity3.address || typeof request.message !== "string" || request.message.length === 0) {
|
|
15538
|
+
throw rejected(
|
|
15539
|
+
"Manage-key signing only permits the canonical sign-in SIWE request"
|
|
15540
|
+
);
|
|
15541
|
+
}
|
|
15542
|
+
}
|
|
15543
|
+
function createOpenKeyManageKeySigningStrategy(options2) {
|
|
15544
|
+
const identity3 = parseCanonicalTinyCloudIdentity(options2.identity);
|
|
15545
|
+
const endpoint = normalizeEndpoint(options2.endpoint);
|
|
15546
|
+
return {
|
|
15547
|
+
type: "callback",
|
|
15548
|
+
openKeyAutoSign: true,
|
|
15549
|
+
handler: async (request) => {
|
|
15550
|
+
assertSignInRequest(request, identity3);
|
|
15551
|
+
const fetchImpl = options2.fetch ?? globalThis.fetch;
|
|
15552
|
+
if (!fetchImpl)
|
|
15553
|
+
throw rejected("OpenKey manage-key signing requires fetch");
|
|
15554
|
+
if (!hasTinyCloudManageKeyScope(options2.scopes)) {
|
|
15555
|
+
throw new OpenKeyManageKeyError(
|
|
15556
|
+
"CONSENT_REQUIRED",
|
|
15557
|
+
"The OAuth bearer token was not granted tinycloud:manage-key"
|
|
15558
|
+
);
|
|
15559
|
+
}
|
|
15560
|
+
const token = await resolveToken(options2.token);
|
|
15561
|
+
const response = await fetchImpl(endpoint, {
|
|
15562
|
+
method: "POST",
|
|
15563
|
+
credentials: "omit",
|
|
15564
|
+
headers: {
|
|
15565
|
+
authorization: `Bearer ${token}`,
|
|
15566
|
+
"content-type": "application/json"
|
|
15567
|
+
},
|
|
15568
|
+
body: JSON.stringify({
|
|
15569
|
+
address: request.address,
|
|
15570
|
+
chainId: request.chainId,
|
|
15571
|
+
message: request.message,
|
|
15572
|
+
type: "siwe"
|
|
15573
|
+
})
|
|
15574
|
+
});
|
|
15575
|
+
let body;
|
|
15576
|
+
try {
|
|
15577
|
+
const value = await response.json();
|
|
15578
|
+
body = isRecord2(value) ? value : void 0;
|
|
15579
|
+
} catch {
|
|
15580
|
+
body = void 0;
|
|
15581
|
+
}
|
|
15582
|
+
if (!response.ok || body?.approved !== true)
|
|
15583
|
+
throw errorForResponse(body, response.status);
|
|
15584
|
+
if (typeof body.signature !== "string")
|
|
15585
|
+
throw rejected("OpenKey manage-key signer did not return a signature");
|
|
15586
|
+
const responseIdentity = parseCanonicalTinyCloudIdentity(
|
|
15587
|
+
body.canonicalIdentity
|
|
15588
|
+
);
|
|
15589
|
+
if (!identitiesEqual(identity3, responseIdentity)) {
|
|
15590
|
+
throw identityMismatch(
|
|
15591
|
+
"OpenKey manage-key signer returned a different canonical identity"
|
|
15592
|
+
);
|
|
15593
|
+
}
|
|
15594
|
+
if (!await verifyEip191MessageSignature(
|
|
15595
|
+
request.message,
|
|
15596
|
+
body.signature,
|
|
15597
|
+
identity3.address
|
|
15598
|
+
)) {
|
|
15599
|
+
throw rejected(
|
|
15600
|
+
"OpenKey manage-key signer returned an invalid signature"
|
|
15601
|
+
);
|
|
15602
|
+
}
|
|
15603
|
+
return { approved: true, signature: body.signature };
|
|
15604
|
+
}
|
|
15605
|
+
};
|
|
15606
|
+
}
|
|
15607
|
+
|
|
15370
15608
|
// src/authorization/spaceCreation.ts
|
|
15371
15609
|
var AutoApproveSpaceCreationHandler = class {
|
|
15372
15610
|
/**
|