decane-connect-kit 2.2.6 → 2.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +112 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +112 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -328,6 +328,13 @@ interface DecaneConnectConfig {
|
|
|
328
328
|
rpName?: string;
|
|
329
329
|
rpcUrls?: Record<string, string>;
|
|
330
330
|
teeUrl?: string;
|
|
331
|
+
/**
|
|
332
|
+
* Persist the live session (handle + JWT) in sessionStorage so a page RELOAD
|
|
333
|
+
* silently resumes it — no repeat passkey/sign-in. Survives reloads, cleared
|
|
334
|
+
* on tab close. Default true. Set false to require a fresh unlock on every
|
|
335
|
+
* page load (stricter, but the reload friction that motivated this returns).
|
|
336
|
+
*/
|
|
337
|
+
persistSession?: boolean;
|
|
331
338
|
/** Offer to save a recovery-share file at signup. Default true. Never blocks onboarding. */
|
|
332
339
|
offerRecoveryAtSignup?: boolean;
|
|
333
340
|
/** Ask the user whether they want a recovery share and, if so, a password for it. */
|
package/dist/index.d.ts
CHANGED
|
@@ -328,6 +328,13 @@ interface DecaneConnectConfig {
|
|
|
328
328
|
rpName?: string;
|
|
329
329
|
rpcUrls?: Record<string, string>;
|
|
330
330
|
teeUrl?: string;
|
|
331
|
+
/**
|
|
332
|
+
* Persist the live session (handle + JWT) in sessionStorage so a page RELOAD
|
|
333
|
+
* silently resumes it — no repeat passkey/sign-in. Survives reloads, cleared
|
|
334
|
+
* on tab close. Default true. Set false to require a fresh unlock on every
|
|
335
|
+
* page load (stricter, but the reload friction that motivated this returns).
|
|
336
|
+
*/
|
|
337
|
+
persistSession?: boolean;
|
|
331
338
|
/** Offer to save a recovery-share file at signup. Default true. Never blocks onboarding. */
|
|
332
339
|
offerRecoveryAtSignup?: boolean;
|
|
333
340
|
/** Ask the user whether they want a recovery share and, if so, a password for it. */
|
package/dist/index.js
CHANGED
|
@@ -2222,6 +2222,22 @@ var TeeClient = class {
|
|
|
2222
2222
|
sessionExpiresIn() {
|
|
2223
2223
|
return this.sessionExpiresInSec;
|
|
2224
2224
|
}
|
|
2225
|
+
/** The opaque session handle, so a live session can be persisted and resumed after a reload. */
|
|
2226
|
+
getHandle() {
|
|
2227
|
+
return this.handle;
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Adopt a handle from a persisted session (page reload) instead of opening a
|
|
2231
|
+
* new one. The session was already attested when first opened, and the handle
|
|
2232
|
+
* is bound to that CVM session — a swapped CVM would simply not recognise it —
|
|
2233
|
+
* so re-attestation is skipped. If the handle has expired server-side, the
|
|
2234
|
+
* first sign() 401s and clears it, falling back to a normal reconnect.
|
|
2235
|
+
*/
|
|
2236
|
+
adoptHandle(handle, assertionPerSignature) {
|
|
2237
|
+
this.handle = handle;
|
|
2238
|
+
this.assertionPerSignature = assertionPerSignature;
|
|
2239
|
+
this.attested = true;
|
|
2240
|
+
}
|
|
2225
2241
|
async ensureAttested() {
|
|
2226
2242
|
if (this.attested) return;
|
|
2227
2243
|
const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
@@ -2402,9 +2418,33 @@ var SessionManager = class {
|
|
|
2402
2418
|
this.solanaAddress = null;
|
|
2403
2419
|
this.expiresAt = null;
|
|
2404
2420
|
this.timeoutId = null;
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2421
|
+
}
|
|
2422
|
+
setExpiryTimer(effectiveMs) {
|
|
2423
|
+
if (this.timeoutId) clearTimeout(this.timeoutId);
|
|
2424
|
+
this.timeoutId = setTimeout(() => {
|
|
2425
|
+
this.end();
|
|
2426
|
+
this.events.emit("session-expired");
|
|
2427
|
+
}, Math.max(0, effectiveMs));
|
|
2428
|
+
}
|
|
2429
|
+
/** The live session handle, for persisting a resumable session across reloads. */
|
|
2430
|
+
getHandle() {
|
|
2431
|
+
return this.tee?.getHandle() ?? null;
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Resume a persisted session without re-authenticating: adopt the handle,
|
|
2435
|
+
* addresses, and expiry from storage. No key material or prompt involved — the
|
|
2436
|
+
* TEE still holds the shares behind this handle. If the handle turns out to be
|
|
2437
|
+
* dead (server restarted, TTL passed), the next sign() fails and the caller
|
|
2438
|
+
* reconnects; nothing here can sign on its own.
|
|
2439
|
+
*/
|
|
2440
|
+
resume(handle, addresses, expiresAtMs) {
|
|
2441
|
+
this.tee = new TeeClient(this.teeUrl);
|
|
2442
|
+
this.tee.adoptHandle(handle, this.requireAssertionPerSignature);
|
|
2443
|
+
this.evmAddress = addresses.evm;
|
|
2444
|
+
this.solanaAddress = addresses.solana;
|
|
2445
|
+
this.expiresAt = expiresAtMs;
|
|
2446
|
+
this.setExpiryTimer(expiresAtMs - Date.now());
|
|
2447
|
+
this.events.emit("session-started");
|
|
2408
2448
|
}
|
|
2409
2449
|
/**
|
|
2410
2450
|
* Opens the TEE session and resolves which addresses this wallet really has.
|
|
@@ -2965,7 +3005,7 @@ function formatBalanceDisplay(value) {
|
|
|
2965
3005
|
// src/social/client.ts
|
|
2966
3006
|
var DEFAULT_API_BASE = "https://backend.decane.app";
|
|
2967
3007
|
var DEFAULT_TEE_URL = "https://4405742df08d19abc549a05ade2a7154d98bd87f-8443.dstack-pha-prod9.phala.network";
|
|
2968
|
-
var DEFAULT_SESSION_MINUTES =
|
|
3008
|
+
var DEFAULT_SESSION_MINUTES = 120;
|
|
2969
3009
|
function makePersistKey(apiKey) {
|
|
2970
3010
|
return `decane:social:${apiKey.slice(0, 20)}`;
|
|
2971
3011
|
}
|
|
@@ -2989,6 +3029,29 @@ function clearPersisted(key) {
|
|
|
2989
3029
|
} catch {
|
|
2990
3030
|
}
|
|
2991
3031
|
}
|
|
3032
|
+
function sessionPersistKey(apiKey) {
|
|
3033
|
+
return `decane:sess:${apiKey.slice(0, 20)}`;
|
|
3034
|
+
}
|
|
3035
|
+
function loadSession(key) {
|
|
3036
|
+
try {
|
|
3037
|
+
const raw = typeof sessionStorage !== "undefined" ? sessionStorage.getItem(key) : null;
|
|
3038
|
+
return raw ? JSON.parse(raw) : null;
|
|
3039
|
+
} catch {
|
|
3040
|
+
return null;
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
function saveSession(key, s) {
|
|
3044
|
+
try {
|
|
3045
|
+
sessionStorage.setItem(key, JSON.stringify(s));
|
|
3046
|
+
} catch {
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
function clearSessionPersist(key) {
|
|
3050
|
+
try {
|
|
3051
|
+
sessionStorage.removeItem(key);
|
|
3052
|
+
} catch {
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
2992
3055
|
async function createDecaneConnect(config) {
|
|
2993
3056
|
if (!config.appId) throw new Error("decane-connect-kit: appId is required");
|
|
2994
3057
|
if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
|
|
@@ -3011,6 +3074,39 @@ async function createDecaneConnect(config) {
|
|
|
3011
3074
|
);
|
|
3012
3075
|
let auth = null;
|
|
3013
3076
|
let persisted = loadPersisted(PERSIST_KEY);
|
|
3077
|
+
const persistSession = config.persistSession !== false;
|
|
3078
|
+
const SESSION_KEY = sessionPersistKey(config.apiKey);
|
|
3079
|
+
function persistLiveSession() {
|
|
3080
|
+
if (!persistSession) return;
|
|
3081
|
+
const handle = session.getHandle();
|
|
3082
|
+
const expiresAt = session.expiresAtTimestamp();
|
|
3083
|
+
if (!handle || !auth || !expiresAt) return;
|
|
3084
|
+
saveSession(SESSION_KEY, {
|
|
3085
|
+
handle,
|
|
3086
|
+
jwt: auth.jwt,
|
|
3087
|
+
userId: auth.userId,
|
|
3088
|
+
addresses: auth.addresses,
|
|
3089
|
+
expiresAt
|
|
3090
|
+
});
|
|
3091
|
+
}
|
|
3092
|
+
function dropLiveSession() {
|
|
3093
|
+
clearSessionPersist(SESSION_KEY);
|
|
3094
|
+
}
|
|
3095
|
+
if (persistSession) {
|
|
3096
|
+
const saved = loadSession(SESSION_KEY);
|
|
3097
|
+
if (saved?.handle && saved.expiresAt > Date.now() + 5e3) {
|
|
3098
|
+
try {
|
|
3099
|
+
session.resume(saved.handle, saved.addresses, saved.expiresAt);
|
|
3100
|
+
auth = { jwt: saved.jwt, userId: saved.userId, addresses: saved.addresses };
|
|
3101
|
+
persisted = { userId: saved.userId, addresses: saved.addresses };
|
|
3102
|
+
} catch {
|
|
3103
|
+
dropLiveSession();
|
|
3104
|
+
}
|
|
3105
|
+
} else if (saved) {
|
|
3106
|
+
dropLiveSession();
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
events.on("session-expired", () => dropLiveSession());
|
|
3014
3110
|
async function pinSetup() {
|
|
3015
3111
|
if (!config.promptPin) throw new PromptPinNotConfiguredError();
|
|
3016
3112
|
const pin = await config.promptPin();
|
|
@@ -3294,6 +3390,7 @@ async function createDecaneConnect(config) {
|
|
|
3294
3390
|
persisted = { userId, addresses };
|
|
3295
3391
|
savePersisted(PERSIST_KEY, persisted);
|
|
3296
3392
|
events.emit("connected");
|
|
3393
|
+
persistLiveSession();
|
|
3297
3394
|
return { addresses, isNewUser: false };
|
|
3298
3395
|
}
|
|
3299
3396
|
async function handleAuthResult(result) {
|
|
@@ -3309,6 +3406,7 @@ async function createDecaneConnect(config) {
|
|
|
3309
3406
|
persisted = { userId: result.userId, addresses };
|
|
3310
3407
|
savePersisted(PERSIST_KEY, persisted);
|
|
3311
3408
|
events.emit("connected");
|
|
3409
|
+
persistLiveSession();
|
|
3312
3410
|
return { addresses, isNewUser: result.isNewUser };
|
|
3313
3411
|
}
|
|
3314
3412
|
const client = {
|
|
@@ -3503,6 +3601,7 @@ async function createDecaneConnect(config) {
|
|
|
3503
3601
|
auth = null;
|
|
3504
3602
|
persisted = null;
|
|
3505
3603
|
clearPersisted(PERSIST_KEY);
|
|
3604
|
+
dropLiveSession();
|
|
3506
3605
|
events.emit("disconnected");
|
|
3507
3606
|
},
|
|
3508
3607
|
getAddresses() {
|
|
@@ -5546,7 +5645,15 @@ function DecaneKit({ config, children }) {
|
|
|
5546
5645
|
sdk.on("wallet-unlocking", () => setSocialAuth((p) => ({ ...p, phase: "unlocking" })));
|
|
5547
5646
|
sdk.on("connected", () => setSocialAuth((p) => ({ ...p, phase: null })));
|
|
5548
5647
|
const saved = sdk.getAddresses();
|
|
5549
|
-
if (
|
|
5648
|
+
if (sdk.isUnlocked()) {
|
|
5649
|
+
setSocialAuth((p) => ({
|
|
5650
|
+
...p,
|
|
5651
|
+
addresses: saved,
|
|
5652
|
+
isUnlocked: true,
|
|
5653
|
+
needsReconnect: false,
|
|
5654
|
+
sessionExpiresAt: sdk.sessionExpiresAt()
|
|
5655
|
+
}));
|
|
5656
|
+
} else if (saved) {
|
|
5550
5657
|
setSocialAuth((p) => ({ ...p, addresses: saved, isUnlocked: false, needsReconnect: true }));
|
|
5551
5658
|
}
|
|
5552
5659
|
sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
|