@tinycloud/node-sdk 3.10.0-beta.3 → 3.11.0-beta.0
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-BpolXzu5.d.ts} +59 -2
- package/dist/{TinyCloudNode-BhWBbCay.d.cts → TinyCloudNode-wAQN1Ndh.d.cts} +59 -2
- package/dist/{authority-WCJA3phs.d.cts → authority-C0JKqTwY.d.cts} +1 -1
- package/dist/{authority-BzqKNzOr.d.ts → authority-Cj8MLCkr.d.ts} +1 -1
- package/dist/core.cjs +100 -24
- 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 +101 -24
- 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 +99 -23
- 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 +100 -23
- 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 +6 -6
|
@@ -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;
|
|
@@ -747,6 +766,21 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
747
766
|
openkeyKeyId?: string;
|
|
748
767
|
/** Caller-supplied display reason, not signed authority. */
|
|
749
768
|
reason?: string;
|
|
769
|
+
/**
|
|
770
|
+
* Let the user change the session expiry in the OpenKey approval. Sent
|
|
771
|
+
* to OpenKey as `expiryChangeable: true` (`@openkey/sdk` >= 0.13.0
|
|
772
|
+
* (beta: 0.13.0-beta.0) forwards it; 0.12.x ignores it). The SIGNED
|
|
773
|
+
* expiry is then the session's expiry.
|
|
774
|
+
*/
|
|
775
|
+
expiryChangeable?: boolean;
|
|
776
|
+
/**
|
|
777
|
+
* The latest signed expiry the caller will accept, used only with `expiryChangeable`: a signed expiry beyond it
|
|
778
|
+
* is refused before any session is built, stored or activated. Epoch MILLISECONDS or a `Date`; a function is
|
|
779
|
+
* called when the result arrives (so a slow approval does not count against it) and returns either. It is not
|
|
780
|
+
* sent to OpenKey and not persisted. It limits a CHANGED expiry only: an expiry equal to the one this SDK
|
|
781
|
+
* requested is accepted, so keep the configured session lifetime within the maximum.
|
|
782
|
+
*/
|
|
783
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
750
784
|
/**
|
|
751
785
|
* Supply another candidate without regenerating the SIWE or session key.
|
|
752
786
|
* Called only after transport or pre-commit verification rejects a result.
|
|
@@ -803,8 +837,18 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
803
837
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
804
838
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
805
839
|
* (resource, action) pair in the returned SIWE.
|
|
840
|
+
*
|
|
841
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
842
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
843
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
844
|
+
* session is built with; the changed expiry must pass
|
|
845
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
846
|
+
* Without it, any expiry difference is refused as in (3).
|
|
806
847
|
*/
|
|
807
|
-
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown
|
|
848
|
+
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown>, options?: {
|
|
849
|
+
expiryChangeable?: boolean;
|
|
850
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
851
|
+
}): Promise<ClientSession>;
|
|
808
852
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
809
853
|
private verifyOpenKeyResult;
|
|
810
854
|
/**
|
|
@@ -1686,7 +1730,7 @@ declare class TinyCloudNode {
|
|
|
1686
1730
|
*/
|
|
1687
1731
|
signInWithOpenKey(authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0], options?: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[1]): Promise<void>;
|
|
1688
1732
|
/** Prepare the OpenKey request before the approval click. */
|
|
1689
|
-
prepareOpenKeySignIn(): Promise<PreparedOpenKeyAuthorization>;
|
|
1733
|
+
prepareOpenKeySignIn(options?: Parameters<NodeUserAuthorization["prepareOpenKeySignIn"]>[0]): Promise<PreparedOpenKeyAuthorization>;
|
|
1690
1734
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
1691
1735
|
signInWithPreparedOpenKey(pending: PreparedOpenKeyAuthorization, authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0]): Promise<void>;
|
|
1692
1736
|
private signInWith;
|
|
@@ -2095,6 +2139,19 @@ declare class TinyCloudNode {
|
|
|
2095
2139
|
* @internal
|
|
2096
2140
|
*/
|
|
2097
2141
|
private getSessionExpiry;
|
|
2142
|
+
/**
|
|
2143
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
2144
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
2145
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
2146
|
+
*
|
|
2147
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
2148
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
2149
|
+
* (the session is already established, so failing sign-in here would be
|
|
2150
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
2151
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
2152
|
+
* @internal
|
|
2153
|
+
*/
|
|
2154
|
+
private getSignedSessionExpiry;
|
|
2098
2155
|
/**
|
|
2099
2156
|
* Wrapper for the WASM createDelegation function.
|
|
2100
2157
|
*
|
|
@@ -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;
|
|
@@ -747,6 +766,21 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
747
766
|
openkeyKeyId?: string;
|
|
748
767
|
/** Caller-supplied display reason, not signed authority. */
|
|
749
768
|
reason?: string;
|
|
769
|
+
/**
|
|
770
|
+
* Let the user change the session expiry in the OpenKey approval. Sent
|
|
771
|
+
* to OpenKey as `expiryChangeable: true` (`@openkey/sdk` >= 0.13.0
|
|
772
|
+
* (beta: 0.13.0-beta.0) forwards it; 0.12.x ignores it). The SIGNED
|
|
773
|
+
* expiry is then the session's expiry.
|
|
774
|
+
*/
|
|
775
|
+
expiryChangeable?: boolean;
|
|
776
|
+
/**
|
|
777
|
+
* The latest signed expiry the caller will accept, used only with `expiryChangeable`: a signed expiry beyond it
|
|
778
|
+
* is refused before any session is built, stored or activated. Epoch MILLISECONDS or a `Date`; a function is
|
|
779
|
+
* called when the result arrives (so a slow approval does not count against it) and returns either. It is not
|
|
780
|
+
* sent to OpenKey and not persisted. It limits a CHANGED expiry only: an expiry equal to the one this SDK
|
|
781
|
+
* requested is accepted, so keep the configured session lifetime within the maximum.
|
|
782
|
+
*/
|
|
783
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
750
784
|
/**
|
|
751
785
|
* Supply another candidate without regenerating the SIWE or session key.
|
|
752
786
|
* Called only after transport or pre-commit verification rejects a result.
|
|
@@ -803,8 +837,18 @@ declare class NodeUserAuthorization implements IUserAuthorization {
|
|
|
803
837
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
804
838
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
805
839
|
* (resource, action) pair in the returned SIWE.
|
|
840
|
+
*
|
|
841
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
842
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
843
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
844
|
+
* session is built with; the changed expiry must pass
|
|
845
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
846
|
+
* Without it, any expiry difference is refused as in (3).
|
|
806
847
|
*/
|
|
807
|
-
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown
|
|
848
|
+
signInWithOpenKeyResult(result: OpenKeyAuthorizationResultV1, prepared: PreparedOpenKeySession, keyId: string, jwk: Record<string, unknown>, options?: {
|
|
849
|
+
expiryChangeable?: boolean;
|
|
850
|
+
maxExpiresAt?: OpenKeyMaxExpiresAt;
|
|
851
|
+
}): Promise<ClientSession>;
|
|
808
852
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
809
853
|
private verifyOpenKeyResult;
|
|
810
854
|
/**
|
|
@@ -1686,7 +1730,7 @@ declare class TinyCloudNode {
|
|
|
1686
1730
|
*/
|
|
1687
1731
|
signInWithOpenKey(authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0], options?: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[1]): Promise<void>;
|
|
1688
1732
|
/** Prepare the OpenKey request before the approval click. */
|
|
1689
|
-
prepareOpenKeySignIn(): Promise<PreparedOpenKeyAuthorization>;
|
|
1733
|
+
prepareOpenKeySignIn(options?: Parameters<NodeUserAuthorization["prepareOpenKeySignIn"]>[0]): Promise<PreparedOpenKeyAuthorization>;
|
|
1690
1734
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
1691
1735
|
signInWithPreparedOpenKey(pending: PreparedOpenKeyAuthorization, authorizeFn: Parameters<NodeUserAuthorization["signInWithOpenKey"]>[0]): Promise<void>;
|
|
1692
1736
|
private signInWith;
|
|
@@ -2095,6 +2139,19 @@ declare class TinyCloudNode {
|
|
|
2095
2139
|
* @internal
|
|
2096
2140
|
*/
|
|
2097
2141
|
private getSessionExpiry;
|
|
2142
|
+
/**
|
|
2143
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
2144
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
2145
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
2146
|
+
*
|
|
2147
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
2148
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
2149
|
+
* (the session is already established, so failing sign-in here would be
|
|
2150
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
2151
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
2152
|
+
* @internal
|
|
2153
|
+
*/
|
|
2154
|
+
private getSignedSessionExpiry;
|
|
2098
2155
|
/**
|
|
2099
2156
|
* Wrapper for the WASM createDelegation function.
|
|
2100
2157
|
*
|
|
@@ -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-wAQN1Ndh.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-BpolXzu5.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]}`;
|
|
@@ -1746,10 +1756,8 @@ var NodeUserAuthorization = class {
|
|
|
1746
1756
|
siwe: prepared.siwe,
|
|
1747
1757
|
signature
|
|
1748
1758
|
};
|
|
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();
|
|
1759
|
+
const expiresAt = lastAnchoredLine(prepared.siwe ?? "", /^Expiration Time: (.+)$/gm) ?? new Date(Date.now() + this.sessionExpirationMs).toISOString();
|
|
1760
|
+
const createdAt = lastAnchoredLine(prepared.siwe ?? "", /^Issued At: (.+)$/gm) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1753
1761
|
const replicaStamp = await this.establishReplicaStamp?.(tinyCloudSession);
|
|
1754
1762
|
const persistedData = this.persistedData(tinyCloudSession, expiresAt, createdAt, replicaStamp);
|
|
1755
1763
|
await this.sessionStorage.save(address, persistedData);
|
|
@@ -1808,6 +1816,7 @@ var NodeUserAuthorization = class {
|
|
|
1808
1816
|
/** Finish address, host and SIWE preparation before an approval click. */
|
|
1809
1817
|
async prepareOpenKeySignIn(options) {
|
|
1810
1818
|
refuseCaveated(this.getCapabilityRequest()?.manifests);
|
|
1819
|
+
assertMaxExpiresAt(options?.maxExpiresAt);
|
|
1811
1820
|
if (options?.host !== void 0) {
|
|
1812
1821
|
const overrideHost = options.host.trim();
|
|
1813
1822
|
if (!overrideHost) {
|
|
@@ -1828,7 +1837,9 @@ var NodeUserAuthorization = class {
|
|
|
1828
1837
|
verificationMethod: prepared.verificationMethod,
|
|
1829
1838
|
host: hostHint,
|
|
1830
1839
|
keyId: options?.openkeyKeyId,
|
|
1831
|
-
presentation
|
|
1840
|
+
presentation,
|
|
1841
|
+
// Present only when set, so the default request shape is unchanged.
|
|
1842
|
+
...options?.expiryChangeable === true ? { expiryChangeable: true } : {}
|
|
1832
1843
|
};
|
|
1833
1844
|
return {
|
|
1834
1845
|
request: originalRequest,
|
|
@@ -1838,7 +1849,10 @@ var NodeUserAuthorization = class {
|
|
|
1838
1849
|
address,
|
|
1839
1850
|
chainId,
|
|
1840
1851
|
generation: this.sessionGeneration,
|
|
1841
|
-
createdAt: Date.now()
|
|
1852
|
+
createdAt: Date.now(),
|
|
1853
|
+
// Kept in memory with the pending request. It is not part of `request`, so it is never sent to OpenKey, and
|
|
1854
|
+
// nothing here is persisted.
|
|
1855
|
+
...options?.maxExpiresAt === void 0 ? {} : { maxExpiresAt: options.maxExpiresAt }
|
|
1842
1856
|
};
|
|
1843
1857
|
}
|
|
1844
1858
|
/** Complete a prepared approval. The caller starts authorization before awaiting this method. */
|
|
@@ -1856,7 +1870,11 @@ var NodeUserAuthorization = class {
|
|
|
1856
1870
|
for (let attempt = 0; ; ) {
|
|
1857
1871
|
try {
|
|
1858
1872
|
result ?? (result = await authorization);
|
|
1859
|
-
signed = await this.verifyOpenKeyResult(result, prepared
|
|
1873
|
+
signed = await this.verifyOpenKeyResult(result, prepared, {
|
|
1874
|
+
// Relax only for a request that actually carried the opt-in.
|
|
1875
|
+
allowExpirationChange: originalRequest.expiryChangeable === true,
|
|
1876
|
+
maxExpiresAt: pending.maxExpiresAt
|
|
1877
|
+
});
|
|
1860
1878
|
signature = result.signature;
|
|
1861
1879
|
break;
|
|
1862
1880
|
} catch (error) {
|
|
@@ -1948,13 +1966,23 @@ var NodeUserAuthorization = class {
|
|
|
1948
1966
|
* in the original prepared SIWE — narrowing OK, broadening rejected.
|
|
1949
1967
|
* 5. Every `result.selectedActionKeys` entry corresponds to a
|
|
1950
1968
|
* (resource, action) pair in the returned SIWE.
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1969
|
+
*
|
|
1970
|
+
* `options.expiryChangeable` is true only when the request sent to OpenKey
|
|
1971
|
+
* carried `expiryChangeable: true`. Then (and only then) the signed
|
|
1972
|
+
* `Expiration Time` may differ from the prepared one and is the expiry the
|
|
1973
|
+
* session is built with; the changed expiry must pass
|
|
1974
|
+
* `checkChangedExpiration` and every other field in (3) must still match.
|
|
1975
|
+
* Without it, any expiry difference is refused as in (3).
|
|
1976
|
+
*/
|
|
1977
|
+
async signInWithOpenKeyResult(result, prepared, keyId, jwk, options) {
|
|
1978
|
+
const signed = await this.verifyOpenKeyResult(result, prepared, {
|
|
1979
|
+
allowExpirationChange: options?.expiryChangeable === true,
|
|
1980
|
+
maxExpiresAt: options?.maxExpiresAt
|
|
1981
|
+
});
|
|
1954
1982
|
return this.signInWithPreparedSession(signed, result.signature, keyId, jwk);
|
|
1955
1983
|
}
|
|
1956
1984
|
/** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
|
|
1957
|
-
async verifyOpenKeyResult(result, prepared) {
|
|
1985
|
+
async verifyOpenKeyResult(result, prepared, options) {
|
|
1958
1986
|
if (result.protocolVersion !== 1) {
|
|
1959
1987
|
throw new Error(`Unsupported OpenKey protocol version ${result.protocolVersion}`);
|
|
1960
1988
|
}
|
|
@@ -1997,16 +2025,39 @@ var NodeUserAuthorization = class {
|
|
|
1997
2025
|
`OpenKey signedMessage.address (${parsedSignedSiwe.address}) does not match local signer address (${expectedAddress})`
|
|
1998
2026
|
);
|
|
1999
2027
|
}
|
|
2028
|
+
const parsedExpiry = parsedSignedSiwe.expirationTime;
|
|
2029
|
+
const lineExpiry = lastAnchoredLine(result.signedMessage, /^Expiration Time: (.+)$/gm);
|
|
2030
|
+
if (parsedExpiry === void 0 || parsedExpiry === null || lineExpiry === void 0) {
|
|
2031
|
+
throw new Error("OpenKey signedMessage has no Expiration Time line of its own");
|
|
2032
|
+
}
|
|
2033
|
+
const parsedExpiryMs = Date.parse(parsedExpiry);
|
|
2034
|
+
if (!Number.isFinite(parsedExpiryMs) || parsedExpiryMs !== Date.parse(lineExpiry)) {
|
|
2035
|
+
throw new Error("OpenKey signedMessage Expiration Time line does not match the message's own expiry");
|
|
2036
|
+
}
|
|
2000
2037
|
const originalHasRecap = /(?:^|\n)-\s+urn:recap:/i.test(prepared.siwe);
|
|
2001
2038
|
const originalFields = (0, import_sdk_core5.extractImmutableSiweFields)(prepared.siwe);
|
|
2002
2039
|
const signedFields = (0, import_sdk_core5.extractImmutableSiweFields)(result.signedMessage);
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2040
|
+
if (options?.allowExpirationChange === true) {
|
|
2041
|
+
const maxExpiresAt = typeof options.maxExpiresAt === "function" ? options.maxExpiresAt() : options.maxExpiresAt;
|
|
2042
|
+
const refusal = (0, import_sdk_core5.checkChangedExpiration)({
|
|
2043
|
+
preparedSiwe: prepared.siwe,
|
|
2044
|
+
signedSiwe: result.signedMessage,
|
|
2045
|
+
now: Date.now(),
|
|
2046
|
+
originalHasRecap,
|
|
2047
|
+
...maxExpiresAt === void 0 ? {} : { maxExpiresAt }
|
|
2048
|
+
});
|
|
2049
|
+
if (refusal !== null) {
|
|
2050
|
+
throw new Error(`OpenKey signedMessage ${refusal}`);
|
|
2051
|
+
}
|
|
2052
|
+
} else {
|
|
2053
|
+
const changedFields = (0, import_sdk_core5.diffImmutableSiweFields)(originalFields, signedFields, {
|
|
2054
|
+
originalHasRecap
|
|
2055
|
+
});
|
|
2056
|
+
if (changedFields.length > 0) {
|
|
2057
|
+
throw new Error(
|
|
2058
|
+
`OpenKey signedMessage altered immutable SIWE fields: ${changedFields.join(", ")}`
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2010
2061
|
}
|
|
2011
2062
|
let originalCaps;
|
|
2012
2063
|
let signedCaps;
|
|
@@ -2287,7 +2338,9 @@ function wireOpenKeyAuthorize(openkey, opts) {
|
|
|
2287
2338
|
host: input.host,
|
|
2288
2339
|
// Forward the presentation envelope verbatim — the OpenKey SDK
|
|
2289
2340
|
// validates and size-bounds it before use.
|
|
2290
|
-
presentation: input.presentation
|
|
2341
|
+
presentation: input.presentation,
|
|
2342
|
+
// Sent only when true, so a request without it keeps its prior shape.
|
|
2343
|
+
...input.expiryChangeable === true ? { expiryChangeable: true } : {}
|
|
2291
2344
|
}, opts);
|
|
2292
2345
|
if (result.protocolVersion !== 1) {
|
|
2293
2346
|
throw new Error(
|
|
@@ -4882,14 +4935,14 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4882
4935
|
await this.signInWith(() => this.auth.signInWithOpenKey(authorizeFn, options));
|
|
4883
4936
|
}
|
|
4884
4937
|
/** Prepare the OpenKey request before the approval click. */
|
|
4885
|
-
async prepareOpenKeySignIn() {
|
|
4938
|
+
async prepareOpenKeySignIn(options) {
|
|
4886
4939
|
if (!this.signer || !this.tc || !this.auth) {
|
|
4887
4940
|
throw new Error("Cannot prepare OpenKey sign-in in session-only mode. Provide a signer in config.");
|
|
4888
4941
|
}
|
|
4889
4942
|
await this.signOutInFlight;
|
|
4890
4943
|
await this.accountRegistryTail;
|
|
4891
4944
|
await this.wasmBindings.ensureInitialized?.();
|
|
4892
|
-
return this.auth.prepareOpenKeySignIn();
|
|
4945
|
+
return this.auth.prepareOpenKeySignIn(options);
|
|
4893
4946
|
}
|
|
4894
4947
|
/** Start OpenKey authorization synchronously, then complete normal session setup. */
|
|
4895
4948
|
signInWithPreparedOpenKey(pending, authorizeFn) {
|
|
@@ -7473,7 +7526,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7473
7526
|
path: "",
|
|
7474
7527
|
// Root access
|
|
7475
7528
|
actions: [...ROOT_DELEGATION_ACTIONS],
|
|
7476
|
-
expiry: this.
|
|
7529
|
+
expiry: this.getSignedSessionExpiry(),
|
|
7477
7530
|
isRevoked: false,
|
|
7478
7531
|
allowSubDelegation: true
|
|
7479
7532
|
};
|
|
@@ -7486,7 +7539,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7486
7539
|
spaceId,
|
|
7487
7540
|
path: "",
|
|
7488
7541
|
actions: [...ROOT_DELEGATION_ACTIONS],
|
|
7489
|
-
expiry: this.
|
|
7542
|
+
expiry: this.getSignedSessionExpiry(),
|
|
7490
7543
|
isRevoked: false,
|
|
7491
7544
|
allowSubDelegation: true
|
|
7492
7545
|
});
|
|
@@ -7578,7 +7631,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7578
7631
|
assertActive: () => graph.assertActive(),
|
|
7579
7632
|
keyProvider: this._keyProvider,
|
|
7580
7633
|
registry: this._capabilityRegistry,
|
|
7581
|
-
sessionExpiry: this.
|
|
7634
|
+
sessionExpiry: this.getSignedSessionExpiry(),
|
|
7582
7635
|
createDelegationWasm: (params) => {
|
|
7583
7636
|
graph.assertActive();
|
|
7584
7637
|
return this.createDelegationWrapper(params);
|
|
@@ -7625,6 +7678,29 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7625
7678
|
const expirationMs = this.config.sessionExpirationMs ?? DEFAULT_SESSION_EXPIRATION_MS;
|
|
7626
7679
|
return new Date(Date.now() + expirationMs);
|
|
7627
7680
|
}
|
|
7681
|
+
/**
|
|
7682
|
+
* The expiry of the session that was actually signed, so the registry root
|
|
7683
|
+
* and the sharing service never outlive or undercut what the SIWE grants
|
|
7684
|
+
* (a user may change the expiry in the OpenKey approval).
|
|
7685
|
+
*
|
|
7686
|
+
* Any expiry the SIWE parses to is used exactly as signed, however near or
|
|
7687
|
+
* far. The configured lifetime is used only when the SIWE cannot be parsed
|
|
7688
|
+
* (the session is already established, so failing sign-in here would be
|
|
7689
|
+
* worse; real sign-ins verify the SIWE first) or when a valid SIWE carries
|
|
7690
|
+
* no `Expiration Time` line, as the other `extractSiweExpiration` callers do.
|
|
7691
|
+
* @internal
|
|
7692
|
+
*/
|
|
7693
|
+
getSignedSessionExpiry() {
|
|
7694
|
+
const siwe = this.auth?.tinyCloudSession?.siwe;
|
|
7695
|
+
if (!siwe) return this.getSessionExpiry();
|
|
7696
|
+
let signed;
|
|
7697
|
+
try {
|
|
7698
|
+
signed = extractSiweExpiration(siwe);
|
|
7699
|
+
} catch {
|
|
7700
|
+
return this.getSessionExpiry();
|
|
7701
|
+
}
|
|
7702
|
+
return signed ?? this.getSessionExpiry();
|
|
7703
|
+
}
|
|
7628
7704
|
/**
|
|
7629
7705
|
* Wrapper for the WASM createDelegation function.
|
|
7630
7706
|
*
|