@gvnrdao/dh-sdk 0.0.309 → 0.0.311
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/browser/dist/browser.js +1 -1
- package/dist/index.js +85 -5
- package/dist/index.mjs +85 -5
- package/dist/interfaces/chunks/config.i.d.ts +8 -0
- package/dist/utils/eip712-login.d.ts +13 -1
- package/dist/utils/server-session-store.d.ts +44 -0
- package/dist/utils/server-session.d.ts +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6527,12 +6527,12 @@ var LOGIN_TYPES_WITH_AUDIENCE = {
|
|
|
6527
6527
|
{ name: "audience", type: "string" }
|
|
6528
6528
|
]
|
|
6529
6529
|
};
|
|
6530
|
-
async function buildSignedLoginPayload(signer, chainId, audience) {
|
|
6530
|
+
async function buildSignedLoginPayload(signer, chainId, audience, reuse) {
|
|
6531
6531
|
const address = await signer.getAddress();
|
|
6532
6532
|
const message = {
|
|
6533
6533
|
address,
|
|
6534
|
-
issuedAt: Math.floor(Date.now() / 1e3),
|
|
6535
|
-
nonce: (0, import_ethers4.hexlify)((0, import_ethers4.randomBytes)(32)),
|
|
6534
|
+
issuedAt: reuse?.issuedAt ?? Math.floor(Date.now() / 1e3),
|
|
6535
|
+
nonce: reuse?.nonce ?? (0, import_ethers4.hexlify)((0, import_ethers4.randomBytes)(32)),
|
|
6536
6536
|
...audience ? { audience } : {}
|
|
6537
6537
|
};
|
|
6538
6538
|
const signature = await signer.signTypedData(
|
|
@@ -6550,6 +6550,13 @@ var STORE_PREFIX = "dh-server-session:";
|
|
|
6550
6550
|
function sessionStoreKey(address, chainId, endpoint) {
|
|
6551
6551
|
return STORE_PREFIX + `${address.trim().toLowerCase()}:${chainId}:${endpoint.replace(/\/+$/, "")}`;
|
|
6552
6552
|
}
|
|
6553
|
+
var PENDING_ENVELOPE_TTL_SECONDS = 8 * 60;
|
|
6554
|
+
function pendingEnvelopeKey(sessionKey) {
|
|
6555
|
+
return sessionKey + ":pending";
|
|
6556
|
+
}
|
|
6557
|
+
function isPendingEnvelopeShape(value) {
|
|
6558
|
+
return !!value && typeof value === "object" && typeof value.issuedAt === "number" && typeof value.nonce === "string";
|
|
6559
|
+
}
|
|
6553
6560
|
function isPayloadShape(value) {
|
|
6554
6561
|
return !!value && typeof value === "object" && typeof value.chainId === "number" && typeof value.signature === "string" && !!value.message && typeof value.message.address === "string" && typeof value.message.issuedAt === "number" && typeof value.message.nonce === "string";
|
|
6555
6562
|
}
|
|
@@ -6591,9 +6598,48 @@ var LocalStorageSessionStore = class {
|
|
|
6591
6598
|
} catch {
|
|
6592
6599
|
}
|
|
6593
6600
|
}
|
|
6601
|
+
loadPending(key) {
|
|
6602
|
+
let raw;
|
|
6603
|
+
try {
|
|
6604
|
+
raw = window.localStorage.getItem(pendingEnvelopeKey(key));
|
|
6605
|
+
} catch {
|
|
6606
|
+
return null;
|
|
6607
|
+
}
|
|
6608
|
+
if (!raw)
|
|
6609
|
+
return null;
|
|
6610
|
+
let parsed;
|
|
6611
|
+
try {
|
|
6612
|
+
parsed = JSON.parse(raw);
|
|
6613
|
+
} catch {
|
|
6614
|
+
this.clearPending(key);
|
|
6615
|
+
return null;
|
|
6616
|
+
}
|
|
6617
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
6618
|
+
if (!isPendingEnvelopeShape(parsed) || nowSec >= parsed.issuedAt + PENDING_ENVELOPE_TTL_SECONDS) {
|
|
6619
|
+
this.clearPending(key);
|
|
6620
|
+
return null;
|
|
6621
|
+
}
|
|
6622
|
+
return parsed;
|
|
6623
|
+
}
|
|
6624
|
+
savePending(key, envelope) {
|
|
6625
|
+
try {
|
|
6626
|
+
window.localStorage.setItem(
|
|
6627
|
+
pendingEnvelopeKey(key),
|
|
6628
|
+
JSON.stringify(envelope)
|
|
6629
|
+
);
|
|
6630
|
+
} catch {
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
clearPending(key) {
|
|
6634
|
+
try {
|
|
6635
|
+
window.localStorage.removeItem(pendingEnvelopeKey(key));
|
|
6636
|
+
} catch {
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6594
6639
|
};
|
|
6595
6640
|
var MemorySessionStore = class {
|
|
6596
6641
|
entries = /* @__PURE__ */ new Map();
|
|
6642
|
+
pending = /* @__PURE__ */ new Map();
|
|
6597
6643
|
load(key) {
|
|
6598
6644
|
return this.entries.get(key) ?? null;
|
|
6599
6645
|
}
|
|
@@ -6603,6 +6649,23 @@ var MemorySessionStore = class {
|
|
|
6603
6649
|
clear(key) {
|
|
6604
6650
|
this.entries.delete(key);
|
|
6605
6651
|
}
|
|
6652
|
+
loadPending(key) {
|
|
6653
|
+
const envelope = this.pending.get(key);
|
|
6654
|
+
if (!envelope)
|
|
6655
|
+
return null;
|
|
6656
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
6657
|
+
if (nowSec >= envelope.issuedAt + PENDING_ENVELOPE_TTL_SECONDS) {
|
|
6658
|
+
this.pending.delete(key);
|
|
6659
|
+
return null;
|
|
6660
|
+
}
|
|
6661
|
+
return envelope;
|
|
6662
|
+
}
|
|
6663
|
+
savePending(key, envelope) {
|
|
6664
|
+
this.pending.set(key, envelope);
|
|
6665
|
+
}
|
|
6666
|
+
clearPending(key) {
|
|
6667
|
+
this.pending.delete(key);
|
|
6668
|
+
}
|
|
6606
6669
|
};
|
|
6607
6670
|
function localStorageUsable() {
|
|
6608
6671
|
try {
|
|
@@ -6640,6 +6703,7 @@ var ServerSession = class {
|
|
|
6640
6703
|
chainId;
|
|
6641
6704
|
store;
|
|
6642
6705
|
onSignaturePrompt;
|
|
6706
|
+
onSignatureResolved;
|
|
6643
6707
|
now;
|
|
6644
6708
|
fetchImpl;
|
|
6645
6709
|
cached = null;
|
|
@@ -6669,6 +6733,7 @@ var ServerSession = class {
|
|
|
6669
6733
|
this.chainId = opts.chainId;
|
|
6670
6734
|
this.store = opts.persistSession === false ? null : opts.sessionStore ?? createDefaultSessionStore();
|
|
6671
6735
|
this.onSignaturePrompt = opts.onSignaturePrompt;
|
|
6736
|
+
this.onSignatureResolved = opts.onSignatureResolved;
|
|
6672
6737
|
this.now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
6673
6738
|
this.fetchImpl = opts.fetchImpl ?? ((...a) => fetch(...a));
|
|
6674
6739
|
}
|
|
@@ -6830,17 +6895,31 @@ var ServerSession = class {
|
|
|
6830
6895
|
this.onSignaturePrompt?.();
|
|
6831
6896
|
let session;
|
|
6832
6897
|
try {
|
|
6898
|
+
const store = this.store;
|
|
6899
|
+
const storeKey = store ? this.lastStoreKey : null;
|
|
6900
|
+
const reuse = storeKey ? store.loadPending?.(storeKey) ?? void 0 : void 0;
|
|
6833
6901
|
const payload = await buildSignedLoginPayload(
|
|
6834
6902
|
this.signer,
|
|
6835
6903
|
this.chainId,
|
|
6836
|
-
this.serviceEndpoint
|
|
6904
|
+
this.serviceEndpoint,
|
|
6905
|
+
reuse
|
|
6837
6906
|
);
|
|
6907
|
+
if (storeKey && !reuse) {
|
|
6908
|
+
store.savePending?.(storeKey, {
|
|
6909
|
+
issuedAt: payload.message.issuedAt,
|
|
6910
|
+
nonce: payload.message.nonce
|
|
6911
|
+
});
|
|
6912
|
+
}
|
|
6838
6913
|
session = await this.login(payload);
|
|
6839
6914
|
if (notCleared()) {
|
|
6840
6915
|
this.persist(payload, session);
|
|
6841
6916
|
this.cached = session;
|
|
6842
6917
|
}
|
|
6918
|
+
if (storeKey)
|
|
6919
|
+
store.clearPending?.(storeKey);
|
|
6920
|
+
this.onSignatureResolved?.(true);
|
|
6843
6921
|
} catch (error) {
|
|
6922
|
+
this.onSignatureResolved?.(false);
|
|
6844
6923
|
const failures = (this.promptBackoff?.failures ?? 0) + 1;
|
|
6845
6924
|
this.promptBackoff = {
|
|
6846
6925
|
failures,
|
|
@@ -16580,7 +16659,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16580
16659
|
chainId: config.chainId,
|
|
16581
16660
|
persistSession: config.sessionPersistence?.enabled,
|
|
16582
16661
|
sessionStore: config.sessionPersistence?.store,
|
|
16583
|
-
onSignaturePrompt: config.onSessionSignaturePrompt
|
|
16662
|
+
onSignaturePrompt: config.onSessionSignaturePrompt,
|
|
16663
|
+
onSignatureResolved: config.onSessionSignatureResolved
|
|
16584
16664
|
});
|
|
16585
16665
|
}
|
|
16586
16666
|
const contractManagerResult = createContractManager({
|
package/dist/index.mjs
CHANGED
|
@@ -6446,12 +6446,12 @@ var LOGIN_TYPES_WITH_AUDIENCE = {
|
|
|
6446
6446
|
{ name: "audience", type: "string" }
|
|
6447
6447
|
]
|
|
6448
6448
|
};
|
|
6449
|
-
async function buildSignedLoginPayload(signer, chainId, audience) {
|
|
6449
|
+
async function buildSignedLoginPayload(signer, chainId, audience, reuse) {
|
|
6450
6450
|
const address = await signer.getAddress();
|
|
6451
6451
|
const message = {
|
|
6452
6452
|
address,
|
|
6453
|
-
issuedAt: Math.floor(Date.now() / 1e3),
|
|
6454
|
-
nonce: hexlify(randomBytes(32)),
|
|
6453
|
+
issuedAt: reuse?.issuedAt ?? Math.floor(Date.now() / 1e3),
|
|
6454
|
+
nonce: reuse?.nonce ?? hexlify(randomBytes(32)),
|
|
6455
6455
|
...audience ? { audience } : {}
|
|
6456
6456
|
};
|
|
6457
6457
|
const signature = await signer.signTypedData(
|
|
@@ -6469,6 +6469,13 @@ var STORE_PREFIX = "dh-server-session:";
|
|
|
6469
6469
|
function sessionStoreKey(address, chainId, endpoint) {
|
|
6470
6470
|
return STORE_PREFIX + `${address.trim().toLowerCase()}:${chainId}:${endpoint.replace(/\/+$/, "")}`;
|
|
6471
6471
|
}
|
|
6472
|
+
var PENDING_ENVELOPE_TTL_SECONDS = 8 * 60;
|
|
6473
|
+
function pendingEnvelopeKey(sessionKey) {
|
|
6474
|
+
return sessionKey + ":pending";
|
|
6475
|
+
}
|
|
6476
|
+
function isPendingEnvelopeShape(value) {
|
|
6477
|
+
return !!value && typeof value === "object" && typeof value.issuedAt === "number" && typeof value.nonce === "string";
|
|
6478
|
+
}
|
|
6472
6479
|
function isPayloadShape(value) {
|
|
6473
6480
|
return !!value && typeof value === "object" && typeof value.chainId === "number" && typeof value.signature === "string" && !!value.message && typeof value.message.address === "string" && typeof value.message.issuedAt === "number" && typeof value.message.nonce === "string";
|
|
6474
6481
|
}
|
|
@@ -6510,9 +6517,48 @@ var LocalStorageSessionStore = class {
|
|
|
6510
6517
|
} catch {
|
|
6511
6518
|
}
|
|
6512
6519
|
}
|
|
6520
|
+
loadPending(key) {
|
|
6521
|
+
let raw;
|
|
6522
|
+
try {
|
|
6523
|
+
raw = window.localStorage.getItem(pendingEnvelopeKey(key));
|
|
6524
|
+
} catch {
|
|
6525
|
+
return null;
|
|
6526
|
+
}
|
|
6527
|
+
if (!raw)
|
|
6528
|
+
return null;
|
|
6529
|
+
let parsed;
|
|
6530
|
+
try {
|
|
6531
|
+
parsed = JSON.parse(raw);
|
|
6532
|
+
} catch {
|
|
6533
|
+
this.clearPending(key);
|
|
6534
|
+
return null;
|
|
6535
|
+
}
|
|
6536
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
6537
|
+
if (!isPendingEnvelopeShape(parsed) || nowSec >= parsed.issuedAt + PENDING_ENVELOPE_TTL_SECONDS) {
|
|
6538
|
+
this.clearPending(key);
|
|
6539
|
+
return null;
|
|
6540
|
+
}
|
|
6541
|
+
return parsed;
|
|
6542
|
+
}
|
|
6543
|
+
savePending(key, envelope) {
|
|
6544
|
+
try {
|
|
6545
|
+
window.localStorage.setItem(
|
|
6546
|
+
pendingEnvelopeKey(key),
|
|
6547
|
+
JSON.stringify(envelope)
|
|
6548
|
+
);
|
|
6549
|
+
} catch {
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
clearPending(key) {
|
|
6553
|
+
try {
|
|
6554
|
+
window.localStorage.removeItem(pendingEnvelopeKey(key));
|
|
6555
|
+
} catch {
|
|
6556
|
+
}
|
|
6557
|
+
}
|
|
6513
6558
|
};
|
|
6514
6559
|
var MemorySessionStore = class {
|
|
6515
6560
|
entries = /* @__PURE__ */ new Map();
|
|
6561
|
+
pending = /* @__PURE__ */ new Map();
|
|
6516
6562
|
load(key) {
|
|
6517
6563
|
return this.entries.get(key) ?? null;
|
|
6518
6564
|
}
|
|
@@ -6522,6 +6568,23 @@ var MemorySessionStore = class {
|
|
|
6522
6568
|
clear(key) {
|
|
6523
6569
|
this.entries.delete(key);
|
|
6524
6570
|
}
|
|
6571
|
+
loadPending(key) {
|
|
6572
|
+
const envelope = this.pending.get(key);
|
|
6573
|
+
if (!envelope)
|
|
6574
|
+
return null;
|
|
6575
|
+
const nowSec = Math.floor(Date.now() / 1e3);
|
|
6576
|
+
if (nowSec >= envelope.issuedAt + PENDING_ENVELOPE_TTL_SECONDS) {
|
|
6577
|
+
this.pending.delete(key);
|
|
6578
|
+
return null;
|
|
6579
|
+
}
|
|
6580
|
+
return envelope;
|
|
6581
|
+
}
|
|
6582
|
+
savePending(key, envelope) {
|
|
6583
|
+
this.pending.set(key, envelope);
|
|
6584
|
+
}
|
|
6585
|
+
clearPending(key) {
|
|
6586
|
+
this.pending.delete(key);
|
|
6587
|
+
}
|
|
6525
6588
|
};
|
|
6526
6589
|
function localStorageUsable() {
|
|
6527
6590
|
try {
|
|
@@ -6559,6 +6622,7 @@ var ServerSession = class {
|
|
|
6559
6622
|
chainId;
|
|
6560
6623
|
store;
|
|
6561
6624
|
onSignaturePrompt;
|
|
6625
|
+
onSignatureResolved;
|
|
6562
6626
|
now;
|
|
6563
6627
|
fetchImpl;
|
|
6564
6628
|
cached = null;
|
|
@@ -6588,6 +6652,7 @@ var ServerSession = class {
|
|
|
6588
6652
|
this.chainId = opts.chainId;
|
|
6589
6653
|
this.store = opts.persistSession === false ? null : opts.sessionStore ?? createDefaultSessionStore();
|
|
6590
6654
|
this.onSignaturePrompt = opts.onSignaturePrompt;
|
|
6655
|
+
this.onSignatureResolved = opts.onSignatureResolved;
|
|
6591
6656
|
this.now = opts.now ?? (() => Math.floor(Date.now() / 1e3));
|
|
6592
6657
|
this.fetchImpl = opts.fetchImpl ?? ((...a) => fetch(...a));
|
|
6593
6658
|
}
|
|
@@ -6749,17 +6814,31 @@ var ServerSession = class {
|
|
|
6749
6814
|
this.onSignaturePrompt?.();
|
|
6750
6815
|
let session;
|
|
6751
6816
|
try {
|
|
6817
|
+
const store = this.store;
|
|
6818
|
+
const storeKey = store ? this.lastStoreKey : null;
|
|
6819
|
+
const reuse = storeKey ? store.loadPending?.(storeKey) ?? void 0 : void 0;
|
|
6752
6820
|
const payload = await buildSignedLoginPayload(
|
|
6753
6821
|
this.signer,
|
|
6754
6822
|
this.chainId,
|
|
6755
|
-
this.serviceEndpoint
|
|
6823
|
+
this.serviceEndpoint,
|
|
6824
|
+
reuse
|
|
6756
6825
|
);
|
|
6826
|
+
if (storeKey && !reuse) {
|
|
6827
|
+
store.savePending?.(storeKey, {
|
|
6828
|
+
issuedAt: payload.message.issuedAt,
|
|
6829
|
+
nonce: payload.message.nonce
|
|
6830
|
+
});
|
|
6831
|
+
}
|
|
6757
6832
|
session = await this.login(payload);
|
|
6758
6833
|
if (notCleared()) {
|
|
6759
6834
|
this.persist(payload, session);
|
|
6760
6835
|
this.cached = session;
|
|
6761
6836
|
}
|
|
6837
|
+
if (storeKey)
|
|
6838
|
+
store.clearPending?.(storeKey);
|
|
6839
|
+
this.onSignatureResolved?.(true);
|
|
6762
6840
|
} catch (error) {
|
|
6841
|
+
this.onSignatureResolved?.(false);
|
|
6763
6842
|
const failures = (this.promptBackoff?.failures ?? 0) + 1;
|
|
6764
6843
|
this.promptBackoff = {
|
|
6765
6844
|
failures,
|
|
@@ -16509,7 +16588,8 @@ var DiamondHandsSDK = class _DiamondHandsSDK {
|
|
|
16509
16588
|
chainId: config.chainId,
|
|
16510
16589
|
persistSession: config.sessionPersistence?.enabled,
|
|
16511
16590
|
sessionStore: config.sessionPersistence?.store,
|
|
16512
|
-
onSignaturePrompt: config.onSessionSignaturePrompt
|
|
16591
|
+
onSignaturePrompt: config.onSessionSignaturePrompt,
|
|
16592
|
+
onSignatureResolved: config.onSessionSignatureResolved
|
|
16513
16593
|
});
|
|
16514
16594
|
}
|
|
16515
16595
|
const contractManagerResult = createContractManager({
|
|
@@ -77,6 +77,14 @@ interface BaseSDKConfig {
|
|
|
77
77
|
* "check your wallet" prompt.
|
|
78
78
|
*/
|
|
79
79
|
onSessionSignaturePrompt?: () => void;
|
|
80
|
+
/**
|
|
81
|
+
* Fires once that signature request has concluded — `true` when a session was
|
|
82
|
+
* established, `false` when it failed or was rejected. Pairs with
|
|
83
|
+
* `onSessionSignaturePrompt`, which otherwise has no ending: a UI that shows
|
|
84
|
+
* "check your wallet" on the prompt has no way to stop showing it, and a
|
|
85
|
+
* contract wallet can leave that request open for minutes.
|
|
86
|
+
*/
|
|
87
|
+
onSessionSignatureResolved?: (ok: boolean) => void;
|
|
80
88
|
ethRpcUrl?: string;
|
|
81
89
|
chainId?: number;
|
|
82
90
|
networkOverride?: {
|
|
@@ -31,4 +31,16 @@ export declare const LOGIN_TYPES_WITH_AUDIENCE: Record<string, TypedDataField[]>
|
|
|
31
31
|
* Audit M-8: pass `audience` (the service endpoint URL being logged into) to bind the login to
|
|
32
32
|
* one service. Omit it for legacy compatibility during the migration window.
|
|
33
33
|
*/
|
|
34
|
-
export declare function buildSignedLoginPayload(signer: Signer, chainId: number, audience?: string
|
|
34
|
+
export declare function buildSignedLoginPayload(signer: Signer, chainId: number, audience?: string,
|
|
35
|
+
/**
|
|
36
|
+
* Reuse the envelope of a login already collecting signatures instead of
|
|
37
|
+
* minting a fresh one. Every field is hashed into the EIP-712 message, so a
|
|
38
|
+
* new nonce produces a new hash — which a multi-owner Safe shows as a new
|
|
39
|
+
* message needing its FIRST signature, discarding the ones already collected.
|
|
40
|
+
* Passing the pending envelope back reproduces the same hash, so the next
|
|
41
|
+
* owner adds to it. See `server-session-store.ts`.
|
|
42
|
+
*/
|
|
43
|
+
reuse?: {
|
|
44
|
+
issuedAt: number;
|
|
45
|
+
nonce: string;
|
|
46
|
+
}): Promise<DhServerLoginPayload>;
|
|
@@ -39,18 +39,62 @@ export interface ServerSessionStore {
|
|
|
39
39
|
load(key: string): PersistedServerSession | null;
|
|
40
40
|
save(key: string, session: PersistedServerSession): void;
|
|
41
41
|
clear(key: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Optional pending-envelope persistence (see `PendingLoginEnvelope`). Optional
|
|
44
|
+
* so a custom store written before this existed still satisfies the interface;
|
|
45
|
+
* without it the session simply mints a fresh envelope each attempt, which is
|
|
46
|
+
* the pre-existing behavior and only costs a multi-owner Safe a restart.
|
|
47
|
+
*/
|
|
48
|
+
loadPending?(key: string): PendingLoginEnvelope | null;
|
|
49
|
+
savePending?(key: string, envelope: PendingLoginEnvelope): void;
|
|
50
|
+
clearPending?(key: string): void;
|
|
42
51
|
}
|
|
43
52
|
export declare function sessionStoreKey(address: string, chainId: number, endpoint: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* The UNSIGNED envelope of a login already collecting signatures.
|
|
55
|
+
*
|
|
56
|
+
* A multi-owner Safe signs across sessions — owner 1 now, owner 2 later — and
|
|
57
|
+
* identifies the pending message solely by its EIP-712 hash. Every field is
|
|
58
|
+
* hashed, so minting a fresh `nonce`/`issuedAt` for the next attempt yields a
|
|
59
|
+
* DIFFERENT hash, which Safe presents as a new message needing its FIRST
|
|
60
|
+
* signature. Whatever owner 1 signed is orphaned, and a 2-of-N Safe can never
|
|
61
|
+
* reach threshold: every retry resets the count to zero.
|
|
62
|
+
*
|
|
63
|
+
* Reusing the envelope makes the re-request hash identically, so Safe shows the
|
|
64
|
+
* same pending message and the next owner ADDS to it. Invisible for an EOA,
|
|
65
|
+
* which signs immediately and spends the envelope.
|
|
66
|
+
*/
|
|
67
|
+
export interface PendingLoginEnvelope {
|
|
68
|
+
issuedAt: number;
|
|
69
|
+
nonce: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* How long an unsigned envelope stays reusable. Must stay UNDER the server's
|
|
73
|
+
* contract-wallet first-use skew (`CONTRACT_ISSUED_AT_SKEW_SECONDS`, 10 min):
|
|
74
|
+
* beyond it the first use is rejected as stale, so reuse would guarantee failure
|
|
75
|
+
* instead of preventing a re-sign.
|
|
76
|
+
*/
|
|
77
|
+
export declare const PENDING_ENVELOPE_TTL_SECONDS: number;
|
|
78
|
+
/** Storage key for the pending envelope matching a session key. */
|
|
79
|
+
export declare function pendingEnvelopeKey(sessionKey: string): string;
|
|
80
|
+
export declare function isPendingEnvelopeShape(value: any): value is PendingLoginEnvelope;
|
|
44
81
|
export declare class LocalStorageSessionStore implements ServerSessionStore {
|
|
45
82
|
load(key: string): PersistedServerSession | null;
|
|
46
83
|
save(key: string, session: PersistedServerSession): void;
|
|
47
84
|
clear(key: string): void;
|
|
85
|
+
loadPending(key: string): PendingLoginEnvelope | null;
|
|
86
|
+
savePending(key: string, envelope: PendingLoginEnvelope): void;
|
|
87
|
+
clearPending(key: string): void;
|
|
48
88
|
}
|
|
49
89
|
export declare class MemorySessionStore implements ServerSessionStore {
|
|
50
90
|
private readonly entries;
|
|
91
|
+
private readonly pending;
|
|
51
92
|
load(key: string): PersistedServerSession | null;
|
|
52
93
|
save(key: string, session: PersistedServerSession): void;
|
|
53
94
|
clear(key: string): void;
|
|
95
|
+
loadPending(key: string): PendingLoginEnvelope | null;
|
|
96
|
+
savePending(key: string, envelope: PendingLoginEnvelope): void;
|
|
97
|
+
clearPending(key: string): void;
|
|
54
98
|
}
|
|
55
99
|
/** `localStorage` when usable (browser, not blocked), else per-instance memory. */
|
|
56
100
|
export declare function createDefaultSessionStore(): ServerSessionStore;
|
|
@@ -40,6 +40,12 @@ export interface ServerSessionOptions {
|
|
|
40
40
|
* never on the silent paths. Lets UIs show a "check your wallet" prompt.
|
|
41
41
|
*/
|
|
42
42
|
onSignaturePrompt?: () => void;
|
|
43
|
+
/**
|
|
44
|
+
* Fires once the prompted signature has concluded, with whether a session was
|
|
45
|
+
* established. Without it `onSignaturePrompt` has no ending, so a UI showing
|
|
46
|
+
* "check your wallet" can never take it down.
|
|
47
|
+
*/
|
|
48
|
+
onSignatureResolved?: (ok: boolean) => void;
|
|
43
49
|
/** Override for tests. */
|
|
44
50
|
now?: () => number;
|
|
45
51
|
/** Override for tests. */
|
|
@@ -56,6 +62,7 @@ export declare class ServerSession {
|
|
|
56
62
|
private readonly chainId;
|
|
57
63
|
private readonly store;
|
|
58
64
|
private readonly onSignaturePrompt?;
|
|
65
|
+
private readonly onSignatureResolved?;
|
|
59
66
|
private readonly now;
|
|
60
67
|
private readonly fetchImpl;
|
|
61
68
|
private cached;
|
package/package.json
CHANGED