decane-connect-kit 2.21.0 → 2.22.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
@@ -398,6 +398,14 @@ interface DecaneConnect {
398
398
  setUnlockPassword(password: string): Promise<void>;
399
399
  /** True when this device can open a session from a password alone. */
400
400
  canUnlockWithPassword(): Promise<boolean>;
401
+ /**
402
+ * "Use a different account." disconnect() keeps one fact about the account
403
+ * that signed out — its userId — so canSignInWithPasskey() and
404
+ * canUnlockWithPassword() can offer that account's own way back in even
405
+ * when several accounts are enrolled on this browser. This drops it. The
406
+ * device shares themselves are untouched.
407
+ */
408
+ forgetLastUser(): void;
401
409
  /** Open a session from the password alone — no provider, no passkey. */
402
410
  unlockWithPassword(password: string): Promise<ConnectResult>;
403
411
  /** Turn the password path off, revoking the token account-wide. */
@@ -1444,6 +1452,7 @@ declare function useSocialWallet(): {
1444
1452
  canUnlockWithPassword: () => Promise<boolean>;
1445
1453
  unlockWithPassword: (password: string) => Promise<void>;
1446
1454
  removeUnlockPassword: () => Promise<void>;
1455
+ forgetLastUser: () => void;
1447
1456
  rotateShares: (opts: {
1448
1457
  password: string;
1449
1458
  passwordHint?: string;
package/dist/index.d.ts CHANGED
@@ -398,6 +398,14 @@ interface DecaneConnect {
398
398
  setUnlockPassword(password: string): Promise<void>;
399
399
  /** True when this device can open a session from a password alone. */
400
400
  canUnlockWithPassword(): Promise<boolean>;
401
+ /**
402
+ * "Use a different account." disconnect() keeps one fact about the account
403
+ * that signed out — its userId — so canSignInWithPasskey() and
404
+ * canUnlockWithPassword() can offer that account's own way back in even
405
+ * when several accounts are enrolled on this browser. This drops it. The
406
+ * device shares themselves are untouched.
407
+ */
408
+ forgetLastUser(): void;
401
409
  /** Open a session from the password alone — no provider, no passkey. */
402
410
  unlockWithPassword(password: string): Promise<ConnectResult>;
403
411
  /** Turn the password path off, revoking the token account-wide. */
@@ -1444,6 +1452,7 @@ declare function useSocialWallet(): {
1444
1452
  canUnlockWithPassword: () => Promise<boolean>;
1445
1453
  unlockWithPassword: (password: string) => Promise<void>;
1446
1454
  removeUnlockPassword: () => Promise<void>;
1455
+ forgetLastUser: () => void;
1447
1456
  rotateShares: (opts: {
1448
1457
  password: string;
1449
1458
  passwordHint?: string;
package/dist/index.js CHANGED
@@ -3607,6 +3607,29 @@ function clearPersisted(key) {
3607
3607
  } catch {
3608
3608
  }
3609
3609
  }
3610
+ function makeLastUserKey(apiKey) {
3611
+ return `${makePersistKey(apiKey)}:last-user`;
3612
+ }
3613
+ function loadLastUser(key) {
3614
+ try {
3615
+ const raw = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
3616
+ return raw ? JSON.parse(raw).userId ?? null : null;
3617
+ } catch {
3618
+ return null;
3619
+ }
3620
+ }
3621
+ function saveLastUser(key, userId) {
3622
+ try {
3623
+ localStorage.setItem(key, JSON.stringify({ userId }));
3624
+ } catch {
3625
+ }
3626
+ }
3627
+ function clearLastUser(key) {
3628
+ try {
3629
+ localStorage.removeItem(key);
3630
+ } catch {
3631
+ }
3632
+ }
3610
3633
  function sessionPersistKey(apiKey) {
3611
3634
  return `decane:sess:${apiKey.slice(0, 20)}`;
3612
3635
  }
@@ -3645,6 +3668,7 @@ async function createDecaneConnect(config) {
3645
3668
  const rpId = config.rpId ?? (typeof window !== "undefined" ? window.location.hostname : "localhost");
3646
3669
  const rpName = config.rpName ?? "Decane Connect";
3647
3670
  const PERSIST_KEY = makePersistKey(config.apiKey);
3671
+ const LAST_USER_KEY = makeLastUserKey(config.apiKey);
3648
3672
  const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
3649
3673
  const events = new EventEmitter();
3650
3674
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
@@ -3657,6 +3681,7 @@ async function createDecaneConnect(config) {
3657
3681
  );
3658
3682
  let auth = null;
3659
3683
  let persisted = loadPersisted(PERSIST_KEY);
3684
+ let lastUserId = loadLastUser(LAST_USER_KEY);
3660
3685
  let ethProvider = null;
3661
3686
  const persistSession = config.persistSession !== false;
3662
3687
  const serverAssistedRecovery = true;
@@ -3802,6 +3827,10 @@ async function createDecaneConnect(config) {
3802
3827
  const stored = await storage.getShare(persisted.userId);
3803
3828
  return hasPasswordUnlock(stored) ? stored : null;
3804
3829
  }
3830
+ if (lastUserId) {
3831
+ const stored = await storage.getShare(lastUserId);
3832
+ return hasPasswordUnlock(stored) ? stored : null;
3833
+ }
3805
3834
  const candidates = (await storage.listShares()).filter(hasPasswordUnlock);
3806
3835
  return candidates.length === 1 ? candidates[0] : null;
3807
3836
  }
@@ -4255,14 +4284,16 @@ async function createDecaneConnect(config) {
4255
4284
  async function passkeyDeviceCandidate() {
4256
4285
  if (!isPasskeySupported()) return null;
4257
4286
  if (persisted?.userId) return passkeyLoginCandidate();
4287
+ if (lastUserId) return usablePasskeyShare(await storage.getShare(lastUserId));
4258
4288
  const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
4259
4289
  return candidates.length === 1 ? candidates[0] : null;
4260
4290
  }
4261
4291
  async function canOfferPasskey() {
4262
4292
  if (!isPasskeySupported()) return false;
4263
4293
  if (await passkeyDeviceCandidate()) return true;
4264
- if (!persisted?.userId) return false;
4265
- return await storage.getShare(persisted.userId) == null;
4294
+ const known = persisted?.userId ?? lastUserId;
4295
+ if (!known) return false;
4296
+ return await storage.getShare(known) == null;
4266
4297
  }
4267
4298
  async function rememberProfile(userId, profile) {
4268
4299
  const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
@@ -4475,8 +4506,9 @@ async function createDecaneConnect(config) {
4475
4506
  async signInWithPasskey() {
4476
4507
  const candidate = await passkeyDeviceCandidate();
4477
4508
  if (candidate) return passkeyLogin(candidate);
4478
- if (isPasskeySupported() && persisted?.userId && await storage.getShare(persisted.userId) == null) {
4479
- return passkeyLoginWithoutShare(persisted.userId);
4509
+ const known = persisted?.userId ?? lastUserId;
4510
+ if (isPasskeySupported() && known && await storage.getShare(known) == null) {
4511
+ return passkeyLoginWithoutShare(known);
4480
4512
  }
4481
4513
  throw new NewDeviceError();
4482
4514
  },
@@ -4816,12 +4848,21 @@ async function createDecaneConnect(config) {
4816
4848
  await api.revoke(auth.jwt).catch(() => {
4817
4849
  });
4818
4850
  }
4851
+ const leaving = auth?.userId ?? persisted?.userId ?? null;
4852
+ if (leaving) {
4853
+ lastUserId = leaving;
4854
+ saveLastUser(LAST_USER_KEY, leaving);
4855
+ }
4819
4856
  auth = null;
4820
4857
  persisted = null;
4821
4858
  clearPersisted(PERSIST_KEY);
4822
4859
  dropLiveSession();
4823
4860
  events.emit("disconnected");
4824
4861
  },
4862
+ forgetLastUser() {
4863
+ lastUserId = null;
4864
+ clearLastUser(LAST_USER_KEY);
4865
+ },
4825
4866
  getAddresses() {
4826
4867
  return auth?.addresses ?? persisted?.addresses ?? null;
4827
4868
  },
@@ -7536,6 +7577,14 @@ function DecaneKit({ config, children }) {
7536
7577
  if (!sdk) return false;
7537
7578
  return sdk.canUnlockWithPassword();
7538
7579
  }, []);
7580
+ const socialForgetLastUser = useCallback3(() => {
7581
+ socialRef.current?.forgetLastUser();
7582
+ const sdk = socialRef.current;
7583
+ if (sdk) {
7584
+ sdk.canSignInWithPasskey().then((can) => setSocialAuth((p) => ({ ...p, canUsePasskey: can }))).catch(() => {
7585
+ });
7586
+ }
7587
+ }, []);
7539
7588
  const socialUnlockWithPassword = useCallback3(async (password) => {
7540
7589
  const sdk = socialRef.current;
7541
7590
  if (!sdk) throw new Error("Social wallet not connected");
@@ -7612,6 +7661,7 @@ function DecaneKit({ config, children }) {
7612
7661
  socialAddPasskey,
7613
7662
  socialSetUnlockPassword,
7614
7663
  socialCanUnlockWithPassword,
7664
+ socialForgetLastUser,
7615
7665
  socialUnlockWithPassword,
7616
7666
  socialRemoveUnlockPassword,
7617
7667
  socialShouldPromptRecovery,
@@ -7921,6 +7971,7 @@ function useSocialWallet() {
7921
7971
  socialAddPasskey,
7922
7972
  socialSetUnlockPassword,
7923
7973
  socialCanUnlockWithPassword,
7974
+ socialForgetLastUser,
7924
7975
  socialUnlockWithPassword,
7925
7976
  socialRemoveUnlockPassword,
7926
7977
  socialShouldPromptRecovery,
@@ -8029,6 +8080,7 @@ function useSocialWallet() {
8029
8080
  throw e;
8030
8081
  }
8031
8082
  }, [socialSetUnlockPassword]);
8083
+ const forgetLastUser = useCallback4(() => socialForgetLastUser(), [socialForgetLastUser]);
8032
8084
  const canUnlockWithPassword = useCallback4(
8033
8085
  () => socialCanUnlockWithPassword(),
8034
8086
  [socialCanUnlockWithPassword]
@@ -8114,6 +8166,8 @@ function useSocialWallet() {
8114
8166
  canUnlockWithPassword,
8115
8167
  unlockWithPassword,
8116
8168
  removeUnlockPassword,
8169
+ // "Use a different account": stop offering the account that signed out.
8170
+ forgetLastUser,
8117
8171
  // Recovery (2-of-3 Shamir share set)
8118
8172
  rotateShares,
8119
8173
  exportPortableBackup,