@tinycloud/node-sdk 3.8.0-beta.19 → 3.8.0-beta.20

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tinycloud/node-sdk
2
2
 
3
+ ## Signed SIWE metadata
4
+
5
+ Set `siweConfig.resources` to `urn:tinycloud:recipients:<base64url>` URI strings to include a recipients declaration in the signed SIWE. The payload uses only unpadded base64url characters (`A–Z`, `a–z`, `0–9`, `_`, `-`). Node-sdk inserts these URIs immediately before the original ReCap resource, which remains last. Ordinary sign-in, external/OpenKey preparation, and bootstrap sessions use the same insertion path; completion and session storage retain the signed message unchanged.
6
+
7
+ These resources do not add permissions. Other schemes, percent encoding, escape sequences and literal controls are not accepted in this option. The generated statement and ReCap remain unchanged; `siweConfig.statement` does not replace the generated statement. Omitting resources or passing an empty array leaves the prepared message unchanged.
8
+
3
9
  ## `@tinycloud/node-sdk/credential-pending-store`
4
10
 
5
11
  File-backed pending store for the email-proof flow. It keeps one file per request under `<home>/credentials/`, named by a hash of the request id. The directory is created with mode 0700 and each file with mode 0600. Permissions and ownership are re-checked on every read. Only a `home` that is itself a symlink is refused; ancestors of `home` may be symlinks, but `realpath(home)` must be owned by the current user. Unsafe files are left in place and reported as an error.
@@ -587,13 +587,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
587
587
  /**
588
588
  * Build SIWE overrides from the top-level nonce and siweConfig.
589
589
  * - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
590
- * - statement is prepended to the default statement
591
- * - resources are appended to the default resources
590
+ * - resources are inserted after WASM preparation, before the final ReCap
592
591
  * - uri triggers a warning (overwriting delegation target)
593
592
  * - all other fields override directly
594
593
  * - per-call nonce overrides siweConfig.nonce when provided
595
594
  */
596
595
  private buildSiweOverrides;
596
+ private prepareSessionWithResources;
597
597
  /**
598
598
  * Add an extension to the authorization flow.
599
599
  */
@@ -587,13 +587,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
587
587
  /**
588
588
  * Build SIWE overrides from the top-level nonce and siweConfig.
589
589
  * - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
590
- * - statement is prepended to the default statement
591
- * - resources are appended to the default resources
590
+ * - resources are inserted after WASM preparation, before the final ReCap
592
591
  * - uri triggers a warning (overwriting delegation target)
593
592
  * - all other fields override directly
594
593
  * - per-call nonce overrides siweConfig.nonce when provided
595
594
  */
596
595
  private buildSiweOverrides;
596
+ private prepareSessionWithResources;
597
597
  /**
598
598
  * Add an extension to the authorization flow.
599
599
  */
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DfuhkGz7.js';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-BRyeZas2.js';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DLuN31i0.cjs';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-NrmztjDd.cjs';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
package/dist/core.cjs CHANGED
@@ -1145,8 +1145,7 @@ var NodeUserAuthorization = class {
1145
1145
  /**
1146
1146
  * Build SIWE overrides from the top-level nonce and siweConfig.
1147
1147
  * - Top-level `nonce` is seeded first so `siweConfig.nonce` wins if both are set.
1148
- * - statement is prepended to the default statement
1149
- * - resources are appended to the default resources
1148
+ * - resources are inserted after WASM preparation, before the final ReCap
1150
1149
  * - uri triggers a warning (overwriting delegation target)
1151
1150
  * - all other fields override directly
1152
1151
  * - per-call nonce overrides siweConfig.nonce when provided
@@ -1164,9 +1163,6 @@ var NodeUserAuthorization = class {
1164
1163
  if (statement) {
1165
1164
  overrides.statement = this.statement ? `${statement} ${this.statement}` : statement;
1166
1165
  }
1167
- if (resources && resources.length > 0) {
1168
- overrides.resources = resources;
1169
- }
1170
1166
  if (uri) {
1171
1167
  console.warn(
1172
1168
  "[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."
@@ -1178,6 +1174,29 @@ var NodeUserAuthorization = class {
1178
1174
  }
1179
1175
  return overrides;
1180
1176
  }
1177
+ prepareSessionWithResources(params) {
1178
+ const resources = this.siweConfig?.resources;
1179
+ if (resources?.some((resource) => typeof resource !== "string" || !/^urn:tinycloud:recipients:[A-Za-z0-9_-]+$/.test(resource))) {
1180
+ throw new Error("SIWE metadata resources must be base64url recipients URIs");
1181
+ }
1182
+ const prepared = this.wasm.prepareSession(params);
1183
+ if (!resources?.length) return prepared;
1184
+ const recapLines = /^- urn:recap:[^\r\n]+$/gim;
1185
+ const recaps = prepared.siwe.match(recapLines);
1186
+ const recap = recaps?.[0];
1187
+ if (recaps?.length !== 1 || !prepared.siwe.endsWith(recap)) {
1188
+ throw new Error("Prepared SIWE must end with exactly one ReCap");
1189
+ }
1190
+ const insertedLines = resources.map((resource) => `- ${resource}`);
1191
+ const siwe = prepared.siwe.slice(0, -recap.length) + insertedLines.join("\n") + "\n" + recap;
1192
+ const expectedFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
1193
+ expectedFields.nonRecapResources = [expectedFields.nonRecapResources, ...insertedLines].filter(Boolean).join("\n");
1194
+ const actualRecaps = siwe.match(recapLines);
1195
+ if (actualRecaps?.length !== 1 || actualRecaps[0] !== recap || (0, import_sdk_core5.diffImmutableSiweFields)(expectedFields, (0, import_sdk_core5.extractImmutableSiweFields)(siwe)).length) {
1196
+ throw new Error("SIWE metadata insertion changed the prepared message");
1197
+ }
1198
+ return { ...prepared, siwe };
1199
+ }
1181
1200
  /**
1182
1201
  * Add an extension to the authorization flow.
1183
1202
  */
@@ -1425,7 +1444,7 @@ var NodeUserAuthorization = class {
1425
1444
  spaceAbilities[spaceId] = (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)(resources);
1426
1445
  }
1427
1446
  const abilities = spaceAbilities[options.spaceId] ?? (0, import_sdk_core5.resourceCapabilitiesToAbilitiesMap)([]);
1428
- const prepared = this.wasm.prepareSession({
1447
+ const prepared = this.prepareSessionWithResources({
1429
1448
  abilities,
1430
1449
  spaceAbilities,
1431
1450
  ...options.rawAbilities !== void 0 ? { rawAbilities: options.rawAbilities } : {},
@@ -1518,7 +1537,7 @@ var NodeUserAuthorization = class {
1518
1537
  const spaceId = capabilityPlan.spaceId;
1519
1538
  const now = /* @__PURE__ */ new Date();
1520
1539
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
1521
- const prepared = this.wasm.prepareSession({
1540
+ const prepared = this.prepareSessionWithResources({
1522
1541
  abilities: capabilityPlan.abilities,
1523
1542
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
1524
1543
  ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},
@@ -1664,7 +1683,7 @@ var NodeUserAuthorization = class {
1664
1683
  const spaceId = capabilityPlan.spaceId;
1665
1684
  const now = /* @__PURE__ */ new Date();
1666
1685
  const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
1667
- const prepared = this.wasm.prepareSession({
1686
+ const prepared = this.prepareSessionWithResources({
1668
1687
  abilities: capabilityPlan.abilities,
1669
1688
  ...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
1670
1689
  ...capabilityPlan.rawAbilities !== void 0 ? { rawAbilities: capabilityPlan.rawAbilities } : {},