@tinycloud/node-sdk 3.10.0 → 3.11.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/{TinyCloudNode-BjJJIifr.d.ts → TinyCloudNode-BlNcvo1c.d.ts} +66 -2
- package/dist/{TinyCloudNode-BhWBbCay.d.cts → TinyCloudNode-CnVaWnAX.d.cts} +66 -2
- package/dist/{authority-WCJA3phs.d.cts → authority-B2SLx27M.d.cts} +1 -1
- package/dist/{authority-BzqKNzOr.d.ts → authority-DHIk5QvV.d.ts} +1 -1
- package/dist/core.cjs +124 -29
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +127 -29
- package/dist/core.js.map +1 -1
- package/dist/encryption-keys.d.cts +1 -1
- package/dist/encryption-keys.d.ts +1 -1
- package/dist/index.cjs +123 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +126 -28
- package/dist/index.js.map +1 -1
- package/dist/replication/authority.d.cts +2 -2
- package/dist/replication/authority.d.ts +2 -2
- package/package.json +2 -2
|
@@ -81,6 +81,12 @@ interface OpenKeyAuthorizeTinyCloud {
|
|
|
81
81
|
* validates and size-bounds it before use.
|
|
82
82
|
*/
|
|
83
83
|
presentation?: CapabilityPresentationEnvelopeV1;
|
|
84
|
+
/**
|
|
85
|
+
* Ask OpenKey to let the user change the session expiry in the approval.
|
|
86
|
+
* Sent only when true. `@openkey/sdk` >= 0.13.0 (beta: 0.13.0-beta.0)
|
|
87
|
+
* forwards it; 0.12.x ignores it.
|
|
88
|
+
*/
|
|
89
|
+
expiryChangeable?: boolean;
|
|
84
90
|
}, opts?: {
|
|
85
91
|
mode?: "iframe" | "popup" | "redirect";
|
|
86
92
|
}): Promise<{
|
|
@@ -114,6 +120,12 @@ interface OpenKeyBridgeInput {
|
|
|
114
120
|
* manifest. Display-only.
|
|
115
121
|
*/
|
|
116
122
|
presentation?: CapabilityPresentationEnvelopeV1;
|
|
123
|
+
/**
|
|
124
|
+
* Ask OpenKey to let the user change the session expiry. Forwarded only
|
|
125
|
+
* when true. The SDK accepts a changed expiry only for a request that
|
|
126
|
+
* carried this flag, and then uses the SIGNED expiry.
|
|
127
|
+
*/
|
|
128
|
+
expiryChangeable?: boolean;
|
|
117
129
|
}
|
|
118
130
|
type NodeUserAuthorizationAuthorizeFn = (input: OpenKeyBridgeInput) => Promise<{
|
|
119
131
|
protocolVersion: 1;
|
|
@@ -160,7 +172,14 @@ interface PreparedOpenKeyAuthorization {
|
|
|
160
172
|
readonly chainId: number;
|
|
161
173
|
readonly generation: number;
|
|
162
174
|
readonly createdAt: number;
|
|
175
|
+
/** The app-level maximum for a changed signed expiry, if one was given. In memory only; never sent or persisted. */
|
|
176
|
+
readonly maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
163
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* A latest acceptable signed expiry: epoch MILLISECONDS, a `Date`, or a function returning either. A function is read
|
|
180
|
+
* when the OpenKey result arrives, not when the request is prepared.
|
|
181
|
+
*/
|
|
182
|
+
type OpenKeyMaxExpiresAt = number | Date | (() => number | Date);
|
|
164
183
|
type OpenKeyAuthorizationResultV1 = Awaited<ReturnType<NodeUserAuthorizationAuthorizeFn>>;
|
|
165
184
|
type PreparedOpenKeySession = {
|
|
166
185
|
siwe: string;
|
|
@@ -636,6 +655,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
636
655
|
private guardSpaceCreation;
|
|
637
656
|
private recordActivationSkippedSpaces;
|
|
638
657
|
createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
|
|
658
|
+
/**
|
|
659
|
+
* When a session that starts at `now` ends. Refused with a dated message when
|
|
660
|
+
* the configured lifetime reaches past what a sign-in message can carry,
|
|
661
|
+
* instead of the bare `RangeError` or WASM parse error `toISOString()` and
|
|
662
|
+
* `prepareSession` would otherwise give.
|
|
663
|
+
*/
|
|
664
|
+
private sessionExpiryFrom;
|
|
639
665
|
private persistedData;
|
|
640
666
|
/**
|
|
641
667
|
* Sign in and create a new session.
|
|
@@ -747,6 +773,21 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
747
773
|
openkeyKeyId?: string;
|
|
748
774
|
/** Caller-supplied display reason, not signed authority. */
|
|
749
775
|
reason?: string;
|
|
776
|
+
/**
|
|
777
|
+
* Let the user change the session expiry in the OpenKey approval. Sent
|
|
778
|
+
* to OpenKey as `expiryChangeable: true` (`@openkey/sdk` >= 0.13.0
|
|
779
|
+
* (beta: 0.13.0-beta.0) forwards it; 0.12.x ignores it). The SIGNED
|
|
780
|
+
* expiry is then the session's expiry.
|
|
781
|
+
*/
|
|
782
|
+
expiryChangeable?: boolean;
|
|
783
|
+
/**
|
|
784
|
+
* The latest signed expiry the caller will accept, used only with `expiryChangeable`: a signed expiry beyond it
|
|
785
|
+
* is refused before any session is built, stored or activated. Epoch MILLISECONDS or a `Date`; a function is
|
|
786
|
+
* called when the result arrives (so a slow approval does not count against it) and returns either. It is not
|
|
787
|
+
* sent to OpenKey and not persisted. It limits a CHANGED expiry only: an expiry equal to the one this SDK
|
|
788
|
+
* requested is accepted, so keep the configured session lifetime within the maximum.
|
|
789
|
+
*/
|
|
790
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
750
791
|
/**
|
|
751
792
|
* Supply another candidate without regenerating the SIWE or session key.
|
|
752
793
|
* Called only after transport or pre-commit verification rejects a result.
|
|
@@ -803,8 +844,18 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
803
844
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
804
845
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
805
846
|
* (resource, action) pair in the returned SIWE.
|
|
847
|
+
*
|
|
848
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
849
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
850
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
851
|
+
* session is built with; the changed expiry must pass
|
|
852
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
853
|
+
* Without it, any expiry difference is refused as in (3).
|
|
806
854
|
*/
|
|
807
|
-
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown
|
|
855
|
+
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown>, options?: {
|
|
856
|
+
expiryChangeable?: boolean;
|
|
857
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
858
|
+
}): Promise<ClientSession>;
|
|
808
859
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
809
860
|
private verifyOpenKeyResult;
|
|
810
861
|
/**
|
|
@@ -1686,7 +1737,7 @@ declare class TinyCloudNode {
|
|
|
1686
1737
|
*/
|
|
1687
1738
|
signInWithOpenKey(authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0], options?: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[1]): Promise<void>;
|
|
1688
1739
|
/** Prepare the OpenKey request before the approval click. */
|
|
1689
|
-
prepareOpenKeySignIn(): Promise<PreparedOpenKeyAuthorization>;
|
|
1740
|
+
prepareOpenKeySignIn(options?: Parameters<NodeUserAuthorization["prepareOpenKeySignIn"]>[0]): Promise<PreparedOpenKeyAuthorization>;
|
|
1690
1741
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
1691
1742
|
signInWithPreparedOpenKey(pending: PreparedOpenKeyAuthorization, authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0]): Promise<void>;
|
|
1692
1743
|
private signInWith;
|
|
@@ -2095,6 +2146,19 @@ declare class TinyCloudNode {
|
|
|
2095
2146
|
* @internal
|
|
2096
2147
|
*/
|
|
2097
2148
|
private getSessionExpiry;
|
|
2149
|
+
/**
|
|
2150
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
2151
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
2152
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
2153
|
+
*
|
|
2154
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
2155
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
2156
|
+
* (the session is already established, so failing sign-in here would be
|
|
2157
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
2158
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
2159
|
+
* @internal
|
|
2160
|
+
*/
|
|
2161
|
+
private getSignedSessionExpiry;
|
|
2098
2162
|
/**
|
|
2099
2163
|
* Wrapper for the WASM createDelegation function.
|
|
2100
2164
|
*
|
|
@@ -81,6 +81,12 @@ interface OpenKeyAuthorizeTinyCloud {
|
|
|
81
81
|
* validates and size-bounds it before use.
|
|
82
82
|
*/
|
|
83
83
|
presentation?: CapabilityPresentationEnvelopeV1;
|
|
84
|
+
/**
|
|
85
|
+
* Ask OpenKey to let the user change the session expiry in the approval.
|
|
86
|
+
* Sent only when true. `@openkey/sdk` >= 0.13.0 (beta: 0.13.0-beta.0)
|
|
87
|
+
* forwards it; 0.12.x ignores it.
|
|
88
|
+
*/
|
|
89
|
+
expiryChangeable?: boolean;
|
|
84
90
|
}, opts?: {
|
|
85
91
|
mode?: "iframe" | "popup" | "redirect";
|
|
86
92
|
}): Promise<{
|
|
@@ -114,6 +120,12 @@ interface OpenKeyBridgeInput {
|
|
|
114
120
|
* manifest. Display-only.
|
|
115
121
|
*/
|
|
116
122
|
presentation?: CapabilityPresentationEnvelopeV1;
|
|
123
|
+
/**
|
|
124
|
+
* Ask OpenKey to let the user change the session expiry. Forwarded only
|
|
125
|
+
* when true. The SDK accepts a changed expiry only for a request that
|
|
126
|
+
* carried this flag, and then uses the SIGNED expiry.
|
|
127
|
+
*/
|
|
128
|
+
expiryChangeable?: boolean;
|
|
117
129
|
}
|
|
118
130
|
type NodeUserAuthorizationAuthorizeFn = (input: OpenKeyBridgeInput) => Promise<{
|
|
119
131
|
protocolVersion: 1;
|
|
@@ -160,7 +172,14 @@ interface PreparedOpenKeyAuthorization {
|
|
|
160
172
|
readonly chainId: number;
|
|
161
173
|
readonly generation: number;
|
|
162
174
|
readonly createdAt: number;
|
|
175
|
+
/** The app-level maximum for a changed signed expiry, if one was given. In memory only; never sent or persisted. */
|
|
176
|
+
readonly maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
163
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* A latest acceptable signed expiry: epoch MILLISECONDS, a `Date`, or a function returning either. A function is read
|
|
180
|
+
* when the OpenKey result arrives, not when the request is prepared.
|
|
181
|
+
*/
|
|
182
|
+
type OpenKeyMaxExpiresAt = number | Date | (() => number | Date);
|
|
164
183
|
type OpenKeyAuthorizationResultV1 = Awaited<ReturnType<NodeUserAuthorizationAuthorizeFn>>;
|
|
165
184
|
type PreparedOpenKeySession = {
|
|
166
185
|
siwe: string;
|
|
@@ -636,6 +655,13 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
636
655
|
private guardSpaceCreation;
|
|
637
656
|
private recordActivationSkippedSpaces;
|
|
638
657
|
createBootstrapSession(options: CreateBootstrapSessionOptions): Promise<TinyCloudSession>;
|
|
658
|
+
/**
|
|
659
|
+
* When a session that starts at `now` ends. Refused with a dated message when
|
|
660
|
+
* the configured lifetime reaches past what a sign-in message can carry,
|
|
661
|
+
* instead of the bare `RangeError` or WASM parse error `toISOString()` and
|
|
662
|
+
* `prepareSession` would otherwise give.
|
|
663
|
+
*/
|
|
664
|
+
private sessionExpiryFrom;
|
|
639
665
|
private persistedData;
|
|
640
666
|
/**
|
|
641
667
|
* Sign in and create a new session.
|
|
@@ -747,6 +773,21 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
747
773
|
openkeyKeyId?: string;
|
|
748
774
|
/** Caller-supplied display reason, not signed authority. */
|
|
749
775
|
reason?: string;
|
|
776
|
+
/**
|
|
777
|
+
* Let the user change the session expiry in the OpenKey approval. Sent
|
|
778
|
+
* to OpenKey as `expiryChangeable: true` (`@openkey/sdk` >= 0.13.0
|
|
779
|
+
* (beta: 0.13.0-beta.0) forwards it; 0.12.x ignores it). The SIGNED
|
|
780
|
+
* expiry is then the session's expiry.
|
|
781
|
+
*/
|
|
782
|
+
expiryChangeable?: boolean;
|
|
783
|
+
/**
|
|
784
|
+
* The latest signed expiry the caller will accept, used only with `expiryChangeable`: a signed expiry beyond it
|
|
785
|
+
* is refused before any session is built, stored or activated. Epoch MILLISECONDS or a `Date`; a function is
|
|
786
|
+
* called when the result arrives (so a slow approval does not count against it) and returns either. It is not
|
|
787
|
+
* sent to OpenKey and not persisted. It limits a CHANGED expiry only: an expiry equal to the one this SDK
|
|
788
|
+
* requested is accepted, so keep the configured session lifetime within the maximum.
|
|
789
|
+
*/
|
|
790
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
750
791
|
/**
|
|
751
792
|
* Supply another candidate without regenerating the SIWE or session key.
|
|
752
793
|
* Called only after transport or pre-commit verification rejects a result.
|
|
@@ -803,8 +844,18 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
803
844
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
804
845
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
805
846
|
* (resource, action) pair in the returned SIWE.
|
|
847
|
+
*
|
|
848
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
849
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
850
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
851
|
+
* session is built with; the changed expiry must pass
|
|
852
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
853
|
+
* Without it, any expiry difference is refused as in (3).
|
|
806
854
|
*/
|
|
807
|
-
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown
|
|
855
|
+
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown>, options?: {
|
|
856
|
+
expiryChangeable?: boolean;
|
|
857
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
858
|
+
}): Promise<ClientSession>;
|
|
808
859
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
809
860
|
private verifyOpenKeyResult;
|
|
810
861
|
/**
|
|
@@ -1686,7 +1737,7 @@ declare class TinyCloudNode {
|
|
|
1686
1737
|
*/
|
|
1687
1738
|
signInWithOpenKey(authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0], options?: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[1]): Promise<void>;
|
|
1688
1739
|
/** Prepare the OpenKey request before the approval click. */
|
|
1689
|
-
prepareOpenKeySignIn(): Promise<PreparedOpenKeyAuthorization>;
|
|
1740
|
+
prepareOpenKeySignIn(options?: Parameters<NodeUserAuthorization["prepareOpenKeySignIn"]>[0]): Promise<PreparedOpenKeyAuthorization>;
|
|
1690
1741
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
1691
1742
|
signInWithPreparedOpenKey(pending: PreparedOpenKeyAuthorization, authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0]): Promise<void>;
|
|
1692
1743
|
private signInWith;
|
|
@@ -2095,6 +2146,19 @@ declare class TinyCloudNode {
|
|
|
2095
2146
|
* @internal
|
|
2096
2147
|
*/
|
|
2097
2148
|
private getSessionExpiry;
|
|
2149
|
+
/**
|
|
2150
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
2151
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
2152
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
2153
|
+
*
|
|
2154
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
2155
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
2156
|
+
* (the session is already established, so failing sign-in here would be
|
|
2157
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
2158
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
2159
|
+
* @internal
|
|
2160
|
+
*/
|
|
2161
|
+
private getSignedSessionExpiry;
|
|
2098
2162
|
/**
|
|
2099
2163
|
* Wrapper for the WASM createDelegation function.
|
|
2100
2164
|
*
|
|
@@ -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-
|
|
4
|
+
import { T as TinyCloudNode } from './TinyCloudNode-CnVaWnAX.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 & {
|
|
@@ -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-
|
|
4
|
+
import { T as TinyCloudNode } from './TinyCloudNode-BlNcvo1c.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 & {
|
package/dist/core.cjs
CHANGED
|
@@ -454,6 +454,16 @@ var import_sdk_core4 = require("@tinycloud/sdk-core");
|
|
|
454
454
|
var defaultSignStrategy = { type: "auto-sign" };
|
|
455
455
|
|
|
456
456
|
// src/authorization/NodeUserAuthorization.ts
|
|
457
|
+
function assertMaxExpiresAt(value) {
|
|
458
|
+
if (value === void 0 || typeof value === "function") return;
|
|
459
|
+
const ms = value instanceof Date ? value.getTime() : value;
|
|
460
|
+
if (typeof ms !== "number" || !Number.isFinite(new Date(ms).getTime())) {
|
|
461
|
+
throw new Error("maxExpiresAt must be a finite number of epoch milliseconds, a valid Date, or a function returning one");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function lastAnchoredLine(siwe, pattern) {
|
|
465
|
+
return [...siwe.matchAll(pattern)].at(-1)?.[1];
|
|
466
|
+
}
|
|
457
467
|
function pkhSpaceIdentity(spaceId) {
|
|
458
468
|
const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(spaceId);
|
|
459
469
|
return match === null ? spaceId : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
|
|
@@ -1427,7 +1437,7 @@ var NodeUserAuthorization = class {
|
|
|
1427
1437
|
}
|
|
1428
1438
|
const jwk = JSON.parse(jwkString);
|
|
1429
1439
|
const now = /* @__PURE__ */ new Date();
|
|
1430
|
-
const expirationTime =
|
|
1440
|
+
const expirationTime = this.sessionExpiryFrom(now);
|
|
1431
1441
|
const primaryIdentity = pkhSpaceIdentity(options.spaceId);
|
|
1432
1442
|
const resourcesBySpace = /* @__PURE__ */ new Map();
|
|
1433
1443
|
for (const resource of options.capabilityRequest.resources) {
|
|
@@ -1482,6 +1492,17 @@ var NodeUserAuthorization = class {
|
|
|
1482
1492
|
signature
|
|
1483
1493
|
};
|
|
1484
1494
|
}
|
|
1495
|
+
/**
|
|
1496
|
+
* When a session that starts at `now` ends. Refused with a dated message when
|
|
1497
|
+
* the configured lifetime reaches past what a sign-in message can carry,
|
|
1498
|
+
* instead of the bare `RangeError` or WASM parse error `toISOString()` and
|
|
1499
|
+
* `prepareSession` would otherwise give.
|
|
1500
|
+
*/
|
|
1501
|
+
sessionExpiryFrom(now) {
|
|
1502
|
+
const expirationTime = new Date(now.getTime() + this.sessionExpirationMs);
|
|
1503
|
+
(0, import_sdk_core5.assertExpiryWithinTechnicalLimit)(expirationTime, "siwe");
|
|
1504
|
+
return expirationTime;
|
|
1505
|
+
}
|
|
1485
1506
|
persistedData(session, expiresAt, createdAt, replicaStamp) {
|
|
1486
1507
|
return {
|
|
1487
1508
|
address: session.address,
|
|
@@ -1537,7 +1558,7 @@ var NodeUserAuthorization = class {
|
|
|
1537
1558
|
const capabilityPlan = this.resolveSignInCapabilities(address, chainId);
|
|
1538
1559
|
const spaceId = capabilityPlan.spaceId;
|
|
1539
1560
|
const now = /* @__PURE__ */ new Date();
|
|
1540
|
-
const expirationTime =
|
|
1561
|
+
const expirationTime = this.sessionExpiryFrom(now);
|
|
1541
1562
|
const prepared = this.prepareSessionWithResources({
|
|
1542
1563
|
abilities: capabilityPlan.abilities,
|
|
1543
1564
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
@@ -1683,7 +1704,7 @@ var NodeUserAuthorization = class {
|
|
|
1683
1704
|
const capabilityPlan = this.resolveSignInCapabilities(address, chainId);
|
|
1684
1705
|
const spaceId = capabilityPlan.spaceId;
|
|
1685
1706
|
const now = /* @__PURE__ */ new Date();
|
|
1686
|
-
const expirationTime =
|
|
1707
|
+
const expirationTime = this.sessionExpiryFrom(now);
|
|
1687
1708
|
const prepared = this.prepareSessionWithResources({
|
|
1688
1709
|
abilities: capabilityPlan.abilities,
|
|
1689
1710
|
...capabilityPlan.spaceAbilities !== void 0 ? { spaceAbilities: capabilityPlan.spaceAbilities } : {},
|
|
@@ -1746,10 +1767,8 @@ var NodeUserAuthorization = class {
|
|
|
1746
1767
|
siwe: prepared.siwe,
|
|
1747
1768
|
signature
|
|
1748
1769
|
};
|
|
1749
|
-
const
|
|
1750
|
-
const
|
|
1751
|
-
const expiresAt = expirationMatch?.[1] ?? new Date(Date.now() + this.sessionExpirationMs).toISOString();
|
|
1752
|
-
const createdAt = issuedAtMatch?.[1] ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1770
|
+
const expiresAt = lastAnchoredLine(prepared.siwe ?? "", /^Expiration Time: (.+)$/gm) ?? this.sessionExpiryFrom(/* @__PURE__ */ new Date()).toISOString();
|
|
1771
|
+
const createdAt = lastAnchoredLine(prepared.siwe ?? "", /^Issued At: (.+)$/gm) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1753
1772
|
const replicaStamp = await this.establishReplicaStamp?.(tinyCloudSession);
|
|
1754
1773
|
const persistedData = this.persistedData(tinyCloudSession, expiresAt, createdAt, replicaStamp);
|
|
1755
1774
|
await this.sessionStorage.save(address, persistedData);
|
|
@@ -1808,6 +1827,7 @@ var NodeUserAuthorization = class {
|
|
|
1808
1827
|
/** Finish address, host and SIWE preparation before an approval click. */
|
|
1809
1828
|
async prepareOpenKeySignIn(options) {
|
|
1810
1829
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
1830
|
+
assertMaxExpiresAt(options?.maxExpiresAt);
|
|
1811
1831
|
if (options?.host !== void 0) {
|
|
1812
1832
|
const overrideHost = options.host.trim();
|
|
1813
1833
|
if (!overrideHost) {
|
|
@@ -1828,7 +1848,9 @@ var NodeUserAuthorization = class {
|
|
|
1828
1848
|
verificationMethod: prepared.verificationMethod,
|
|
1829
1849
|
host: hostHint,
|
|
1830
1850
|
keyId: options?.openkeyKeyId,
|
|
1831
|
-
presentation
|
|
1851
|
+
presentation,
|
|
1852
|
+
// Present only when set, so the default request shape is unchanged.
|
|
1853
|
+
...options?.expiryChangeable === true ? { expiryChangeable: true } : {}
|
|
1832
1854
|
};
|
|
1833
1855
|
return {
|
|
1834
1856
|
request: originalRequest,
|
|
@@ -1838,7 +1860,10 @@ var NodeUserAuthorization = class {
|
|
|
1838
1860
|
address,
|
|
1839
1861
|
chainId,
|
|
1840
1862
|
generation: this.sessionGeneration,
|
|
1841
|
-
createdAt: Date.now()
|
|
1863
|
+
createdAt: Date.now(),
|
|
1864
|
+
// Kept in memory with the pending request. It is not part of `request`, so it is never sent to OpenKey, and
|
|
1865
|
+
// nothing here is persisted.
|
|
1866
|
+
...options?.maxExpiresAt === void 0 ? {} : { maxExpiresAt: options.maxExpiresAt }
|
|
1842
1867
|
};
|
|
1843
1868
|
}
|
|
1844
1869
|
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
@@ -1856,7 +1881,11 @@ var NodeUserAuthorization = class {
|
|
|
1856
1881
|
for (let attempt = 0; ; ) {
|
|
1857
1882
|
try {
|
|
1858
1883
|
result ?? (result = await authorization);
|
|
1859
|
-
signed = await this.verifyOpenKeyResult(result, prepared
|
|
1884
|
+
signed = await this.verifyOpenKeyResult(result, prepared, {
|
|
1885
|
+
// Relax only for a request that actually carried the opt-in.
|
|
1886
|
+
allowExpirationChange: originalRequest.expiryChangeable === true,
|
|
1887
|
+
maxExpiresAt: pending.maxExpiresAt
|
|
1888
|
+
});
|
|
1860
1889
|
signature = result.signature;
|
|
1861
1890
|
break;
|
|
1862
1891
|
} catch (error) {
|
|
@@ -1948,13 +1977,23 @@ var NodeUserAuthorization = class {
|
|
|
1948
1977
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
1949
1978
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
1950
1979
|
* (resource, action) pair in the returned SIWE.
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1980
|
+
*
|
|
1981
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
1982
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
1983
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
1984
|
+
* session is built with; the changed expiry must pass
|
|
1985
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
1986
|
+
* Without it, any expiry difference is refused as in (3).
|
|
1987
|
+
*/
|
|
1988
|
+
async signInWithOpenKeyResult(result, prepared, keyId, jwk, options) {
|
|
1989
|
+
const signed = await this.verifyOpenKeyResult(result, prepared, {
|
|
1990
|
+
allowExpirationChange: options?.expiryChangeable === true,
|
|
1991
|
+
maxExpiresAt: options?.maxExpiresAt
|
|
1992
|
+
});
|
|
1954
1993
|
return this.signInWithPreparedSession(signed, result.signature, keyId, jwk);
|
|
1955
1994
|
}
|
|
1956
1995
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
1957
|
-
async verifyOpenKeyResult(result, prepared) {
|
|
1996
|
+
async verifyOpenKeyResult(result, prepared, options) {
|
|
1958
1997
|
if (result.protocolVersion !== 1) {
|
|
1959
1998
|
throw new Error(`Unsupported OpenKey protocol version ${result.protocolVersion}`);
|
|
1960
1999
|
}
|
|
@@ -1997,16 +2036,39 @@ var NodeUserAuthorization = class {
|
|
|
1997
2036
|
`OpenKey signedMessage.address (${parsedSignedSiwe.address}) does not match local signer address (${expectedAddress})`
|
|
1998
2037
|
);
|
|
1999
2038
|
}
|
|
2039
|
+
const parsedExpiry = parsedSignedSiwe.expirationTime;
|
|
2040
|
+
const lineExpiry = lastAnchoredLine(result.signedMessage, /^Expiration Time: (.+)$/gm);
|
|
2041
|
+
if (parsedExpiry === void 0 || parsedExpiry === null || lineExpiry === void 0) {
|
|
2042
|
+
throw new Error("OpenKey signedMessage has no Expiration Time line of its own");
|
|
2043
|
+
}
|
|
2044
|
+
const parsedExpiryMs = Date.parse(parsedExpiry);
|
|
2045
|
+
if (!Number.isFinite(parsedExpiryMs) || parsedExpiryMs !== Date.parse(lineExpiry)) {
|
|
2046
|
+
throw new Error("OpenKey signedMessage Expiration Time line does not match the message's own expiry");
|
|
2047
|
+
}
|
|
2000
2048
|
const originalHasRecap = /(?:^|\n)-\s+urn:recap:/i.test(prepared.siwe);
|
|
2001
2049
|
const originalFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
|
|
2002
2050
|
const signedFields = (0, import_sdk_core5.extractImmutableSiweFields)(result.signedMessage);
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2051
|
+
if (options?.allowExpirationChange === true) {
|
|
2052
|
+
const maxExpiresAt = typeof options.maxExpiresAt === "function" ? options.maxExpiresAt() : options.maxExpiresAt;
|
|
2053
|
+
const refusal = (0, import_sdk_core5.checkChangedExpiration)({
|
|
2054
|
+
preparedSiwe: prepared.siwe,
|
|
2055
|
+
signedSiwe: result.signedMessage,
|
|
2056
|
+
now: Date.now(),
|
|
2057
|
+
originalHasRecap,
|
|
2058
|
+
...maxExpiresAt === void 0 ? {} : { maxExpiresAt }
|
|
2059
|
+
});
|
|
2060
|
+
if (refusal !== null) {
|
|
2061
|
+
throw new Error(`OpenKey signedMessage ${refusal}`);
|
|
2062
|
+
}
|
|
2063
|
+
} else {
|
|
2064
|
+
const changedFields = (0, import_sdk_core5.diffImmutableSiweFields)(originalFields, signedFields, {
|
|
2065
|
+
originalHasRecap
|
|
2066
|
+
});
|
|
2067
|
+
if (changedFields.length > 0) {
|
|
2068
|
+
throw new Error(
|
|
2069
|
+
`OpenKey signedMessage altered immutable SIWE fields: ${changedFields.join(", ")}`
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2010
2072
|
}
|
|
2011
2073
|
let originalCaps;
|
|
2012
2074
|
let signedCaps;
|
|
@@ -2287,7 +2349,9 @@ function wireOpenKeyAuthorize(openkey, opts) {
|
|
|
2287
2349
|
host: input.host,
|
|
2288
2350
|
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
2289
2351
|
// validates and size-bounds it before use.
|
|
2290
|
-
presentation: input.presentation
|
|
2352
|
+
presentation: input.presentation,
|
|
2353
|
+
// Sent only when true, so a request without it keeps its prior shape.
|
|
2354
|
+
...input.expiryChangeable === true ? { expiryChangeable: true } : {}
|
|
2291
2355
|
}, opts);
|
|
2292
2356
|
if (result.protocolVersion !== 1) {
|
|
2293
2357
|
throw new Error(
|
|
@@ -4882,14 +4946,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4882
4946
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
4883
4947
|
}
|
|
4884
4948
|
/** Prepare the OpenKey request before the approval click. */
|
|
4885
|
-
async prepareOpenKeySignIn() {
|
|
4949
|
+
async prepareOpenKeySignIn(options) {
|
|
4886
4950
|
if (!this.signer || !this.tc || !this.auth) {
|
|
4887
4951
|
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
4888
4952
|
}
|
|
4889
4953
|
await this.signOutInFlight;
|
|
4890
4954
|
await this.accountRegistryTail;
|
|
4891
4955
|
await this.wasmBindings.ensureInitialized?.();
|
|
4892
|
-
return this.auth.prepareOpenKeySignIn();
|
|
4956
|
+
return this.auth.prepareOpenKeySignIn(options);
|
|
4893
4957
|
}
|
|
4894
4958
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
4895
4959
|
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
@@ -7473,7 +7537,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7473
7537
|
path: "",
|
|
7474
7538
|
// Root access
|
|
7475
7539
|
actions: [...ROOT_DELEGATION_ACTIONS],
|
|
7476
|
-
expiry: this.
|
|
7540
|
+
expiry: this.getSignedSessionExpiry(),
|
|
7477
7541
|
isRevoked: false,
|
|
7478
7542
|
allowSubDelegation: true
|
|
7479
7543
|
};
|
|
@@ -7486,7 +7550,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7486
7550
|
spaceId,
|
|
7487
7551
|
path: "",
|
|
7488
7552
|
actions: [...ROOT_DELEGATION_ACTIONS],
|
|
7489
|
-
expiry: this.
|
|
7553
|
+
expiry: this.getSignedSessionExpiry(),
|
|
7490
7554
|
isRevoked: false,
|
|
7491
7555
|
allowSubDelegation: true
|
|
7492
7556
|
});
|
|
@@ -7578,7 +7642,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7578
7642
|
assertActive: () => graph.assertActive(),
|
|
7579
7643
|
keyProvider: this._keyProvider,
|
|
7580
7644
|
registry: this._capabilityRegistry,
|
|
7581
|
-
sessionExpiry: this.
|
|
7645
|
+
sessionExpiry: this.getSignedSessionExpiry(),
|
|
7582
7646
|
createDelegationWasm: (params) => {
|
|
7583
7647
|
graph.assertActive();
|
|
7584
7648
|
return this.createDelegationWrapper(params);
|
|
@@ -7625,6 +7689,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7625
7689
|
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
7626
7690
|
return new Date(Date.now() + expirationMs);
|
|
7627
7691
|
}
|
|
7692
|
+
/**
|
|
7693
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
7694
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
7695
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
7696
|
+
*
|
|
7697
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
7698
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
7699
|
+
* (the session is already established, so failing sign-in here would be
|
|
7700
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
7701
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
7702
|
+
* @internal
|
|
7703
|
+
*/
|
|
7704
|
+
getSignedSessionExpiry() {
|
|
7705
|
+
const siwe = this.auth?.tinyCloudSession?.siwe;
|
|
7706
|
+
if (!siwe) return this.getSessionExpiry();
|
|
7707
|
+
let signed;
|
|
7708
|
+
try {
|
|
7709
|
+
signed = extractSiweExpiration(siwe);
|
|
7710
|
+
} catch {
|
|
7711
|
+
return this.getSessionExpiry();
|
|
7712
|
+
}
|
|
7713
|
+
return signed ?? this.getSessionExpiry();
|
|
7714
|
+
}
|
|
7628
7715
|
/**
|
|
7629
7716
|
* Wrapper for the WASM createDelegation function.
|
|
7630
7717
|
*
|
|
@@ -7654,6 +7741,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7654
7741
|
// DID here and rejects the fragment as an invalid audience DID.
|
|
7655
7742
|
verificationMethod: params.session.verificationMethod.split("#", 1)[0]
|
|
7656
7743
|
};
|
|
7744
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(params.expirationSecs * 1e3, "ucan");
|
|
7657
7745
|
const result = this.wasmBindings.createDelegation(
|
|
7658
7746
|
wasmSession,
|
|
7659
7747
|
params.delegateDID,
|
|
@@ -7686,9 +7774,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7686
7774
|
}
|
|
7687
7775
|
const permissions = ownerDelegationPermissions(params);
|
|
7688
7776
|
const now = /* @__PURE__ */ new Date();
|
|
7689
|
-
if (!(params.expiresAt instanceof Date) || params.expiresAt.getTime() <= now.getTime()
|
|
7690
|
-
throw new Error("Owner delegation expiry must be explicit, future, and
|
|
7777
|
+
if (!(params.expiresAt instanceof Date) || params.expiresAt.getTime() <= now.getTime()) {
|
|
7778
|
+
throw new Error("Owner delegation expiry must be explicit, future, and a valid date");
|
|
7691
7779
|
}
|
|
7780
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(params.expiresAt, "siwe");
|
|
7692
7781
|
if (!this.signer) throw new Error("Owner wallet signer is required");
|
|
7693
7782
|
const session = this.currentTinyCloudSession();
|
|
7694
7783
|
if (!session) throw new Error("Owner session is required");
|
|
@@ -7789,6 +7878,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7789
7878
|
* not sent.
|
|
7790
7879
|
*/
|
|
7791
7880
|
async authorizeShareDeliveryV3(input) {
|
|
7881
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(new Date(input.expiresAt), "siwe");
|
|
7792
7882
|
const envelopeRef = await (0, import_sdk_core11.shortDeliveryLinkBodyCid)(input.shareUrl, input.trustedOrigins);
|
|
7793
7883
|
const session = this.currentTinyCloudSession();
|
|
7794
7884
|
const serviceSession = this._serviceContext?.session;
|
|
@@ -7841,6 +7931,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7841
7931
|
};
|
|
7842
7932
|
}
|
|
7843
7933
|
async createRootDelegationForSharing(params, assertActive) {
|
|
7934
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(params.requestedExpiry, "siwe");
|
|
7844
7935
|
try {
|
|
7845
7936
|
return (await this.createOwnerDelegation({
|
|
7846
7937
|
delegateDid: params.shareKeyDID,
|
|
@@ -8537,6 +8628,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8537
8628
|
if (sessionExpiry !== void 0 && sessionExpiry < expiresAt) {
|
|
8538
8629
|
expiresAt = sessionExpiry;
|
|
8539
8630
|
}
|
|
8631
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(expiresAt, "siwe");
|
|
8540
8632
|
const delegations = [];
|
|
8541
8633
|
let rawEntriesAttached = false;
|
|
8542
8634
|
for (const [spaceId, entries] of bySpace) {
|
|
@@ -10009,6 +10101,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
10009
10101
|
const now = /* @__PURE__ */ new Date();
|
|
10010
10102
|
const expiryMs = params.expiryMs ?? 60 * 60 * 1e3;
|
|
10011
10103
|
const expirationTime = new Date(now.getTime() + expiryMs);
|
|
10104
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(expirationTime, "siwe");
|
|
10012
10105
|
const prepared = this.wasmBindings.prepareSession({
|
|
10013
10106
|
abilities,
|
|
10014
10107
|
address: this.wasmBindings.ensureEip55(session.address),
|
|
@@ -10163,6 +10256,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
10163
10256
|
const { abilities, rawAbilities } = this.buildActivationAbilities(delegation);
|
|
10164
10257
|
const now = /* @__PURE__ */ new Date();
|
|
10165
10258
|
const expirationTime = delegation.expiry;
|
|
10259
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(expirationTime, "siwe");
|
|
10166
10260
|
const prepared = this.wasmBindings.prepareSession({
|
|
10167
10261
|
abilities,
|
|
10168
10262
|
address: this.wasmBindings.ensureEip55(mySession.address),
|
|
@@ -10282,6 +10376,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
10282
10376
|
const now = /* @__PURE__ */ new Date();
|
|
10283
10377
|
const requestedExpiry = params.expiryMs === void 0 ? parentDelegation.expiry : new Date(now.getTime() + params.expiryMs);
|
|
10284
10378
|
const actualExpiry = requestedExpiry > parentDelegation.expiry ? parentDelegation.expiry : requestedExpiry;
|
|
10379
|
+
(0, import_sdk_core11.assertExpiryWithinTechnicalLimit)(actualExpiry, "siwe");
|
|
10285
10380
|
const abilities = {};
|
|
10286
10381
|
const rawAbilities = {};
|
|
10287
10382
|
for (const resource of requestedResources) {
|