decane-connect-kit 2.24.1 → 2.25.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/index.d.cts CHANGED
@@ -159,6 +159,27 @@ interface DecaneConnectConfig {
159
159
  * has happened. Default false: the share is wrapped and stored at creation.
160
160
  */
161
161
  deferDeviceProtection?: boolean;
162
+ /**
163
+ * Resume a live session in a NEW TAB, not just after a reload. Default false.
164
+ *
165
+ * The session — handle plus JWT — normally lives in sessionStorage, so it
166
+ * survives a reload and dies with the tab. That makes a second tab, or a
167
+ * return visit an hour later, a full sign-in and unlock again even though the
168
+ * enclave session is still valid for two hours.
169
+ *
170
+ * With this on, the session is held in localStorage under the same expiry, so
171
+ * a returning user is signing again in about a second.
172
+ *
173
+ * THE TRADE, stated plainly: the handle IS the signing credential — no other
174
+ * header is required to sign with it. Widening its storage widens the window
175
+ * in which anything able to run script on your origin could take it, from one
176
+ * tab to every tab until it expires. It is off by default for that reason.
177
+ *
178
+ * Turn it on when the return-visit cost matters more than that window, and
179
+ * pair it with `requireAssertionPerSignature` so a stolen handle still cannot
180
+ * move funds without a fresh authenticator touch.
181
+ */
182
+ resumeSessionAcrossTabs?: boolean;
162
183
  /**
163
184
  * Offer to save a recovery-share file at signup. **Default false** since
164
185
  * 2.7.4 — opt in explicitly with `true`.
package/dist/index.d.ts CHANGED
@@ -159,6 +159,27 @@ interface DecaneConnectConfig {
159
159
  * has happened. Default false: the share is wrapped and stored at creation.
160
160
  */
161
161
  deferDeviceProtection?: boolean;
162
+ /**
163
+ * Resume a live session in a NEW TAB, not just after a reload. Default false.
164
+ *
165
+ * The session — handle plus JWT — normally lives in sessionStorage, so it
166
+ * survives a reload and dies with the tab. That makes a second tab, or a
167
+ * return visit an hour later, a full sign-in and unlock again even though the
168
+ * enclave session is still valid for two hours.
169
+ *
170
+ * With this on, the session is held in localStorage under the same expiry, so
171
+ * a returning user is signing again in about a second.
172
+ *
173
+ * THE TRADE, stated plainly: the handle IS the signing credential — no other
174
+ * header is required to sign with it. Widening its storage widens the window
175
+ * in which anything able to run script on your origin could take it, from one
176
+ * tab to every tab until it expires. It is off by default for that reason.
177
+ *
178
+ * Turn it on when the return-visit cost matters more than that window, and
179
+ * pair it with `requireAssertionPerSignature` so a stolen handle still cannot
180
+ * move funds without a fresh authenticator touch.
181
+ */
182
+ resumeSessionAcrossTabs?: boolean;
162
183
  /**
163
184
  * Offer to save a recovery-share file at signup. **Default false** since
164
185
  * 2.7.4 — opt in explicitly with `true`.
package/dist/index.js CHANGED
@@ -2331,9 +2331,62 @@ import { sha256, encodeBase58 } from "ethers";
2331
2331
  // src/social/teeClient.ts
2332
2332
  import { startAuthentication } from "@simplewebauthn/browser";
2333
2333
 
2334
+ // src/social/attestation.ts
2335
+ import {
2336
+ getCollateral,
2337
+ getCollateralAndVerify,
2338
+ verify as verifyQuoteWithCollateral,
2339
+ PHALA_PCCS_URL
2340
+ } from "@phala/dcap-qvl";
2341
+
2342
+ // src/social/collateralCache.ts
2343
+ var CACHE_PREFIX = "decane:collateral:v1:";
2344
+ var COLLATERAL_TTL_MS = 12 * 60 * 60 * 1e3;
2345
+ function storage() {
2346
+ try {
2347
+ return typeof localStorage !== "undefined" ? localStorage : null;
2348
+ } catch {
2349
+ return null;
2350
+ }
2351
+ }
2352
+ function keyFor(teeUrl) {
2353
+ return `${CACHE_PREFIX}${teeUrl}`;
2354
+ }
2355
+ function readCollateral(teeUrl) {
2356
+ const store = storage();
2357
+ if (!store) return null;
2358
+ try {
2359
+ const raw = store.getItem(keyFor(teeUrl));
2360
+ if (!raw) return null;
2361
+ const entry = JSON.parse(raw);
2362
+ if (!entry?.at || Date.now() - entry.at > COLLATERAL_TTL_MS) {
2363
+ store.removeItem(keyFor(teeUrl));
2364
+ return null;
2365
+ }
2366
+ return entry.collateral ?? null;
2367
+ } catch {
2368
+ return null;
2369
+ }
2370
+ }
2371
+ function writeCollateral(teeUrl, collateral) {
2372
+ const store = storage();
2373
+ if (!store) return;
2374
+ try {
2375
+ store.setItem(keyFor(teeUrl), JSON.stringify({ at: Date.now(), collateral }));
2376
+ } catch {
2377
+ }
2378
+ }
2379
+ function dropCollateral(teeUrl) {
2380
+ const store = storage();
2381
+ if (!store) return;
2382
+ try {
2383
+ store.removeItem(keyFor(teeUrl));
2384
+ } catch {
2385
+ }
2386
+ }
2387
+
2334
2388
  // src/social/attestation.ts
2335
2389
  init_errors();
2336
- import { getCollateralAndVerify } from "@phala/dcap-qvl";
2337
2390
 
2338
2391
  // ../../infra/tee-measurement.json
2339
2392
  var tee_measurement_default = {
@@ -2360,6 +2413,9 @@ function hexToBytes(hex) {
2360
2413
  function bytesToHex(bytes) {
2361
2414
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2362
2415
  }
2416
+ function nowSeconds() {
2417
+ return Math.floor(Date.now() / 1e3);
2418
+ }
2363
2419
  async function verifyTeeAttestation(teeUrl, nonce) {
2364
2420
  const res = await fetch(`${teeUrl}/attestation`, {
2365
2421
  method: "POST",
@@ -2369,9 +2425,22 @@ async function verifyTeeAttestation(teeUrl, nonce) {
2369
2425
  if (!res.ok) throw new AttestationError("quote fetch failed");
2370
2426
  const { quote, measurement } = await res.json();
2371
2427
  let verified;
2428
+ const cached = readCollateral(teeUrl);
2372
2429
  try {
2373
- verified = await getCollateralAndVerify(hexToBytes(quote));
2374
- } catch {
2430
+ if (cached) {
2431
+ try {
2432
+ verified = await verifyQuoteWithCollateral(hexToBytes(quote), cached, nowSeconds());
2433
+ } catch {
2434
+ dropCollateral(teeUrl);
2435
+ verified = await getCollateralAndVerify(hexToBytes(quote));
2436
+ }
2437
+ } else {
2438
+ const collateral = await getCollateral(PHALA_PCCS_URL, hexToBytes(quote));
2439
+ verified = await verifyQuoteWithCollateral(hexToBytes(quote), collateral, nowSeconds());
2440
+ writeCollateral(teeUrl, collateral);
2441
+ }
2442
+ } catch (e) {
2443
+ if (e instanceof AttestationError) throw e;
2375
2444
  throw new AttestationError("quote signature invalid");
2376
2445
  }
2377
2446
  if (verified.status === "Revoked") {
@@ -2413,14 +2482,51 @@ async function teeFetch(url, init) {
2413
2482
  }
2414
2483
  var ATTESTATION_TTL_MS = 10 * 6e4;
2415
2484
  var attestationCache = /* @__PURE__ */ new Map();
2485
+ var ATTESTATION_PERSIST_PREFIX = "decane:attested:v1:";
2486
+ function persistedAt(teeUrl) {
2487
+ try {
2488
+ if (typeof sessionStorage === "undefined") return null;
2489
+ const raw = sessionStorage.getItem(ATTESTATION_PERSIST_PREFIX + teeUrl);
2490
+ const at = raw ? Number(raw) : NaN;
2491
+ return Number.isFinite(at) ? at : null;
2492
+ } catch {
2493
+ return null;
2494
+ }
2495
+ }
2496
+ function rememberAttested(teeUrl, at) {
2497
+ try {
2498
+ if (typeof sessionStorage !== "undefined") {
2499
+ sessionStorage.setItem(ATTESTATION_PERSIST_PREFIX + teeUrl, String(at));
2500
+ }
2501
+ } catch {
2502
+ }
2503
+ }
2504
+ function forgetAttested(teeUrl) {
2505
+ try {
2506
+ if (typeof sessionStorage !== "undefined") {
2507
+ sessionStorage.removeItem(ATTESTATION_PERSIST_PREFIX + teeUrl);
2508
+ }
2509
+ } catch {
2510
+ }
2511
+ }
2416
2512
  function attestOnce(teeUrl, verify) {
2417
2513
  const hit = attestationCache.get(teeUrl);
2418
2514
  if (hit && Date.now() - hit.at < ATTESTATION_TTL_MS) return hit.promise;
2419
- const promise = verify().catch((e) => {
2515
+ const at = persistedAt(teeUrl);
2516
+ if (at && Date.now() - at < ATTESTATION_TTL_MS) {
2517
+ const resolved = Promise.resolve();
2518
+ attestationCache.set(teeUrl, { at, promise: resolved });
2519
+ return resolved;
2520
+ }
2521
+ const startedAt = Date.now();
2522
+ const promise = verify().then(() => {
2523
+ rememberAttested(teeUrl, startedAt);
2524
+ }).catch((e) => {
2420
2525
  attestationCache.delete(teeUrl);
2526
+ forgetAttested(teeUrl);
2421
2527
  throw e;
2422
2528
  });
2423
- attestationCache.set(teeUrl, { at: Date.now(), promise });
2529
+ attestationCache.set(teeUrl, { at: startedAt, promise });
2424
2530
  return promise;
2425
2531
  }
2426
2532
  function prewarmAttestation(teeUrl) {
@@ -3692,29 +3798,44 @@ function clearLastUser(key) {
3692
3798
  function sessionPersistKey(apiKey) {
3693
3799
  return `decane:sess:${apiKey.slice(0, 20)}`;
3694
3800
  }
3801
+ var sessionStore = () => typeof sessionStorage !== "undefined" ? sessionStorage : null;
3802
+ function useCrossTabSessionStore(enabled) {
3803
+ sessionStore = enabled ? () => typeof localStorage !== "undefined" ? localStorage : null : () => typeof sessionStorage !== "undefined" ? sessionStorage : null;
3804
+ }
3695
3805
  function loadSession(key) {
3696
3806
  try {
3697
- const raw = typeof sessionStorage !== "undefined" ? sessionStorage.getItem(key) : null;
3698
- return raw ? JSON.parse(raw) : null;
3807
+ const raw = sessionStore()?.getItem(key) ?? (typeof sessionStorage !== "undefined" ? sessionStorage.getItem(key) : null) ?? (typeof localStorage !== "undefined" ? localStorage.getItem(key) : null);
3808
+ if (!raw) return null;
3809
+ const parsed = JSON.parse(raw);
3810
+ if (parsed?.expiresAt && parsed.expiresAt <= Date.now()) {
3811
+ clearSessionPersist(key);
3812
+ return null;
3813
+ }
3814
+ return parsed;
3699
3815
  } catch {
3700
3816
  return null;
3701
3817
  }
3702
3818
  }
3703
3819
  function saveSession(key, s) {
3704
3820
  try {
3705
- sessionStorage.setItem(key, JSON.stringify(s));
3821
+ sessionStore()?.setItem(key, JSON.stringify(s));
3706
3822
  } catch {
3707
3823
  }
3708
3824
  }
3709
3825
  function clearSessionPersist(key) {
3710
3826
  try {
3711
- sessionStorage.removeItem(key);
3827
+ if (typeof sessionStorage !== "undefined") sessionStorage.removeItem(key);
3828
+ } catch {
3829
+ }
3830
+ try {
3831
+ if (typeof localStorage !== "undefined") localStorage.removeItem(key);
3712
3832
  } catch {
3713
3833
  }
3714
3834
  }
3715
3835
  async function createDecaneConnect(config) {
3716
3836
  if (!config.appId) throw new Error("decane-connect-kit: appId is required");
3717
3837
  if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
3838
+ useCrossTabSessionStore(config.resumeSessionAcrossTabs === true);
3718
3839
  if (!config.chains?.length)
3719
3840
  throw new Error("decane-connect-kit: at least one chain is required");
3720
3841
  if (config.rotateOnNewDeviceRecovery && !config.onRecoveryRotated) {
@@ -3733,7 +3854,7 @@ async function createDecaneConnect(config) {
3733
3854
  const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
3734
3855
  const events = new EventEmitter();
3735
3856
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
3736
- const storage = new Storage();
3857
+ const storage2 = new Storage();
3737
3858
  const session = new SessionManager(
3738
3859
  events,
3739
3860
  durationMs,
@@ -3876,7 +3997,7 @@ async function createDecaneConnect(config) {
3876
3997
  }
3877
3998
  async function attachUnlockToken(userId, jwt, tokenKey) {
3878
3999
  try {
3879
- const stored = await storage.getShare(userId);
4000
+ const stored = await storage2.getShare(userId);
3880
4001
  if (!stored) return;
3881
4002
  const { token } = await api.issueUnlockToken(jwt);
3882
4003
  const unlockTokenIv = generateRandomBytes(12);
@@ -3885,13 +4006,13 @@ async function createDecaneConnect(config) {
3885
4006
  new TextEncoder().encode(token),
3886
4007
  unlockTokenIv
3887
4008
  );
3888
- await storage.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
4009
+ await storage2.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
3889
4010
  } catch (e) {
3890
4011
  console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
3891
4012
  }
3892
4013
  }
3893
4014
  async function persistRotatedDeviceShare(userId, deviceShare) {
3894
- const stored = await storage.getShare(userId);
4015
+ const stored = await storage2.getShare(userId);
3895
4016
  if (!stored) {
3896
4017
  throw new CallSequenceError(
3897
4018
  "Share set was rotated but this device has no stored share to update. Recover the wallet on this device before rotating."
@@ -3900,21 +4021,21 @@ async function createDecaneConnect(config) {
3900
4021
  const wrappingKey = await getWrappingKey(stored);
3901
4022
  const iv = generateRandomBytes(12);
3902
4023
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3903
- await storage.putShare({ ...stored, wrappedDeviceShare, iv });
4024
+ await storage2.putShare({ ...stored, wrappedDeviceShare, iv });
3904
4025
  }
3905
4026
  function hasPasswordUnlock(stored) {
3906
4027
  return !!stored && stored.unlockMethod === "password" && !!stored.passwordSalt && !!stored.wrappedUnlockToken && !!stored.unlockTokenIv;
3907
4028
  }
3908
4029
  async function passwordDeviceCandidate() {
3909
4030
  if (persisted?.userId) {
3910
- const stored = await storage.getShare(persisted.userId);
4031
+ const stored = await storage2.getShare(persisted.userId);
3911
4032
  return hasPasswordUnlock(stored) ? stored : null;
3912
4033
  }
3913
4034
  if (lastUserId) {
3914
- const stored = await storage.getShare(lastUserId);
4035
+ const stored = await storage2.getShare(lastUserId);
3915
4036
  return hasPasswordUnlock(stored) ? stored : null;
3916
4037
  }
3917
- const candidates = (await storage.listShares()).filter(hasPasswordUnlock);
4038
+ const candidates = (await storage2.listShares()).filter(hasPasswordUnlock);
3918
4039
  return candidates.length === 1 ? candidates[0] : null;
3919
4040
  }
3920
4041
  async function getWrappingKey(stored) {
@@ -3942,7 +4063,7 @@ async function createDecaneConnect(config) {
3942
4063
  let credentialId;
3943
4064
  let prfOutput;
3944
4065
  try {
3945
- const label = (await storage.getShare(userId))?.profile?.email;
4066
+ const label = (await storage2.getShare(userId))?.profile?.email;
3946
4067
  ({ credentialId, prfOutput } = await registerWebAuthnCredential({
3947
4068
  api,
3948
4069
  jwt,
@@ -3974,8 +4095,8 @@ async function createDecaneConnect(config) {
3974
4095
  const wrappingKey = await importAesKey(prfOutput);
3975
4096
  const iv = generateRandomBytes(12);
3976
4097
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3977
- const existing = await storage.getShare(userId);
3978
- await storage.putShare({
4098
+ const existing = await storage2.getShare(userId);
4099
+ await storage2.putShare({
3979
4100
  ...existing,
3980
4101
  userId,
3981
4102
  wrappedDeviceShare,
@@ -4059,7 +4180,7 @@ async function createDecaneConnect(config) {
4059
4180
  const { wrappingKey, tokenKey, meta } = await passwordSetup();
4060
4181
  const iv = generateRandomBytes(12);
4061
4182
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4062
- await storage.putShare({
4183
+ await storage2.putShare({
4063
4184
  userId,
4064
4185
  wrappedDeviceShare,
4065
4186
  iv,
@@ -4133,7 +4254,7 @@ async function createDecaneConnect(config) {
4133
4254
  shareSet.device,
4134
4255
  iv
4135
4256
  );
4136
- await storage.putShare({
4257
+ await storage2.putShare({
4137
4258
  userId,
4138
4259
  wrappedDeviceShare,
4139
4260
  iv,
@@ -4237,7 +4358,7 @@ async function createDecaneConnect(config) {
4237
4358
  async function returningUserUnlock(userId, jwt) {
4238
4359
  const rawAddresses = await api.getAddresses(jwt);
4239
4360
  const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
4240
- const stored = await storage.getShare(userId);
4361
+ const stored = await storage2.getShare(userId);
4241
4362
  let deviceShare;
4242
4363
  let joinedViaPasskeySync = false;
4243
4364
  if (!stored) {
@@ -4276,7 +4397,7 @@ async function createDecaneConnect(config) {
4276
4397
  rpId,
4277
4398
  teeUrl,
4278
4399
  api,
4279
- storage,
4400
+ storage: storage2,
4280
4401
  onProgress: config.onRecoveryProgress,
4281
4402
  promptForRecoveryFile: config.promptForRecoveryFile,
4282
4403
  onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
@@ -4290,7 +4411,7 @@ async function createDecaneConnect(config) {
4290
4411
  });
4291
4412
  deviceShare = recovered.deviceShare;
4292
4413
  joinedViaPasskeySync = recovered.flow === "passkey-sync";
4293
- if (!deferDeviceProtection && !await storage.getShare(userId) && isPasskeySupported()) {
4414
+ if (!deferDeviceProtection && !await storage2.getShare(userId) && isPasskeySupported()) {
4294
4415
  await registerPasskeyAndBackup(userId, jwt, deviceShare);
4295
4416
  }
4296
4417
  } else {
@@ -4311,7 +4432,7 @@ async function createDecaneConnect(config) {
4311
4432
  } catch (e) {
4312
4433
  if (e instanceof StaleDeviceShareError) {
4313
4434
  zeroize(deviceShare);
4314
- await storage.deleteShare(userId);
4435
+ await storage2.deleteShare(userId);
4315
4436
  if (stored) return returningUserUnlock(userId, jwt);
4316
4437
  } else {
4317
4438
  zeroize(deviceShare);
@@ -4335,7 +4456,7 @@ async function createDecaneConnect(config) {
4335
4456
  const result = await tee.rotateShares(jwt);
4336
4457
  try {
4337
4458
  await persistRotatedDeviceShare(userId, result.deviceShare);
4338
- const refreshed = await storage.getShare(userId);
4459
+ const refreshed = await storage2.getShare(userId);
4339
4460
  if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
4340
4461
  await api.putRecoveryBackup(jwt, {
4341
4462
  wrappedDeviceShare: refreshed.wrappedDeviceShare,
@@ -4368,13 +4489,13 @@ async function createDecaneConnect(config) {
4368
4489
  }
4369
4490
  async function passkeyLoginCandidate() {
4370
4491
  if (!persisted?.userId || !isPasskeySupported()) return null;
4371
- return usablePasskeyShare(await storage.getShare(persisted.userId));
4492
+ return usablePasskeyShare(await storage2.getShare(persisted.userId));
4372
4493
  }
4373
4494
  async function passkeyDeviceCandidate() {
4374
4495
  if (!isPasskeySupported()) return null;
4375
4496
  if (persisted?.userId) return passkeyLoginCandidate();
4376
- if (lastUserId) return usablePasskeyShare(await storage.getShare(lastUserId));
4377
- const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
4497
+ if (lastUserId) return usablePasskeyShare(await storage2.getShare(lastUserId));
4498
+ const candidates = (await storage2.listShares()).map(usablePasskeyShare).filter(Boolean);
4378
4499
  return candidates.length === 1 ? candidates[0] : null;
4379
4500
  }
4380
4501
  async function canOfferPasskey() {
@@ -4382,15 +4503,15 @@ async function createDecaneConnect(config) {
4382
4503
  if (await passkeyDeviceCandidate()) return true;
4383
4504
  const known = persisted?.userId ?? lastUserId;
4384
4505
  if (!known) return false;
4385
- return await storage.getShare(known) == null;
4506
+ return await storage2.getShare(known) == null;
4386
4507
  }
4387
4508
  async function rememberProfile(userId, profile) {
4388
4509
  const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
4389
4510
  if (!Object.keys(incoming).length) return;
4390
4511
  try {
4391
- const stored = await storage.getShare(userId);
4512
+ const stored = await storage2.getShare(userId);
4392
4513
  if (!stored) return;
4393
- await storage.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4514
+ await storage2.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4394
4515
  } catch {
4395
4516
  }
4396
4517
  }
@@ -4414,7 +4535,7 @@ async function createDecaneConnect(config) {
4414
4535
  savePersisted(PERSIST_KEY, persisted);
4415
4536
  events.emit("connected");
4416
4537
  persistLiveSession();
4417
- const stored = await storage.getShare(userId);
4538
+ const stored = await storage2.getShare(userId);
4418
4539
  return {
4419
4540
  addresses,
4420
4541
  isNewUser: false,
@@ -4587,7 +4708,7 @@ async function createDecaneConnect(config) {
4587
4708
  events.emit("connected");
4588
4709
  persistLiveSession();
4589
4710
  scheduleAccessTokenExpiry(token, claims.expiresAt);
4590
- const stored = await storage.getShare(claims.userId);
4711
+ const stored = await storage2.getShare(claims.userId);
4591
4712
  return {
4592
4713
  addresses,
4593
4714
  isNewUser: false,
@@ -4617,7 +4738,7 @@ async function createDecaneConnect(config) {
4617
4738
  const candidate = await passkeyDeviceCandidate();
4618
4739
  if (candidate) return passkeyLogin(candidate);
4619
4740
  const known = persisted?.userId ?? lastUserId;
4620
- if (isPasskeySupported() && known && await storage.getShare(known) == null) {
4741
+ if (isPasskeySupported() && known && await storage2.getShare(known) == null) {
4621
4742
  return passkeyLoginWithoutShare(known);
4622
4743
  }
4623
4744
  throw new NewDeviceError();
@@ -4636,7 +4757,7 @@ async function createDecaneConnect(config) {
4636
4757
  async setUnlockPassword(password) {
4637
4758
  if (!auth) throw new ReconnectRequiredError();
4638
4759
  validateUnlockPassword(password);
4639
- const stored = await storage.getShare(auth.userId);
4760
+ const stored = await storage2.getShare(auth.userId);
4640
4761
  if (!stored) throw new NewDeviceError();
4641
4762
  const deviceShare = await aesGcmDecrypt(
4642
4763
  await getWrappingKey(stored),
@@ -4655,7 +4776,7 @@ async function createDecaneConnect(config) {
4655
4776
  );
4656
4777
  const iv = generateRandomBytes(12);
4657
4778
  const wrappedDeviceShare = await aesGcmEncrypt(shareKey, deviceShare, iv);
4658
- await storage.putShare({
4779
+ await storage2.putShare({
4659
4780
  ...stored,
4660
4781
  unlockMethod: "password",
4661
4782
  wrappedDeviceShare,
@@ -4735,9 +4856,9 @@ async function createDecaneConnect(config) {
4735
4856
  async removeUnlockPassword() {
4736
4857
  if (!auth) throw new ReconnectRequiredError();
4737
4858
  await api.revokeUnlockTokens(auth.jwt);
4738
- const stored = await storage.getShare(auth.userId);
4859
+ const stored = await storage2.getShare(auth.userId);
4739
4860
  if (!stored || stored.unlockMethod !== "password") return;
4740
- await storage.putShare({
4861
+ await storage2.putShare({
4741
4862
  ...stored,
4742
4863
  wrappedUnlockToken: void 0,
4743
4864
  unlockTokenIv: void 0
@@ -4745,13 +4866,13 @@ async function createDecaneConnect(config) {
4745
4866
  },
4746
4867
  async hasPasskey() {
4747
4868
  if (!auth) return false;
4748
- const stored = await storage.getShare(auth.userId);
4869
+ const stored = await storage2.getShare(auth.userId);
4749
4870
  return stored?.unlockMethod === "passkey";
4750
4871
  },
4751
4872
  async addPasskey() {
4752
4873
  if (!auth) throw new ReconnectRequiredError();
4753
4874
  if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
4754
- const stored = await storage.getShare(auth.userId);
4875
+ const stored = await storage2.getShare(auth.userId);
4755
4876
  if (stored?.unlockMethod === "passkey") return;
4756
4877
  if (!stored) {
4757
4878
  if (!serverAssistedRecovery) throw new NewDeviceError();
@@ -4784,11 +4905,11 @@ async function createDecaneConnect(config) {
4784
4905
  },
4785
4906
  async deviceProtected() {
4786
4907
  if (!auth) return false;
4787
- return await storage.getShare(auth.userId) !== null;
4908
+ return await storage2.getShare(auth.userId) !== null;
4788
4909
  },
4789
4910
  async protectDevice() {
4790
4911
  if (!auth) throw new ReconnectRequiredError();
4791
- if (await storage.getShare(auth.userId)) return true;
4912
+ if (await storage2.getShare(auth.userId)) return true;
4792
4913
  if (!serverAssistedRecovery) throw new NewDeviceError();
4793
4914
  const tee = new TeeClient(teeUrl);
4794
4915
  const provisioned = await tee.provisionDeviceShare(auth.jwt);
@@ -4798,7 +4919,7 @@ async function createDecaneConnect(config) {
4798
4919
  } finally {
4799
4920
  zeroize(provisioned);
4800
4921
  }
4801
- return await storage.getShare(auth.userId) !== null;
4922
+ return await storage2.getShare(auth.userId) !== null;
4802
4923
  },
4803
4924
  async unlock() {
4804
4925
  if (!auth) {