@bounded-sh/client 0.0.111 → 0.0.112-ux-audit.2

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.
Files changed (67) hide show
  1. package/dist/auth/index.d.ts +3 -1
  2. package/dist/auth/oidc-auth.d.ts +3 -1
  3. package/dist/auth/providers/email-auth-provider.d.ts +1 -1
  4. package/dist/auth/providers/guest-auth-provider.d.ts +1 -1
  5. package/dist/auth/providers/injected-evm-wallet-provider.d.ts +1 -1
  6. package/dist/auth/providers/injected-wallet-provider.d.ts +1 -1
  7. package/dist/auth/providers/offchain-auth-provider.d.ts +1 -1
  8. package/dist/auth/providers/privy-expo-provider.d.ts +1 -1
  9. package/dist/{email-auth-provider-DxS2WYxM.js → email-auth-provider-53sA5_i2.js} +4 -3
  10. package/dist/email-auth-provider-53sA5_i2.js.map +1 -0
  11. package/dist/{email-auth-provider-CVp6hSCo.mjs → email-auth-provider-BfCH7K6N.mjs} +4 -3
  12. package/dist/email-auth-provider-BfCH7K6N.mjs.map +1 -0
  13. package/dist/{email-auth-provider-BfBvF_4M.mjs → email-auth-provider-C7RXLVd9.mjs} +4 -3
  14. package/dist/email-auth-provider-C7RXLVd9.mjs.map +1 -0
  15. package/dist/{email-auth-provider-CiqhQ1c2.js → email-auth-provider-DTrCfOJd.js} +4 -3
  16. package/dist/email-auth-provider-DTrCfOJd.js.map +1 -0
  17. package/dist/{guest-auth-provider-DDBXeHoo.js → guest-auth-provider-00KkSNpE.js} +10 -5
  18. package/dist/guest-auth-provider-00KkSNpE.js.map +1 -0
  19. package/dist/{guest-auth-provider-Bm_yqQF6.mjs → guest-auth-provider-2J4fnTX5.mjs} +10 -5
  20. package/dist/guest-auth-provider-2J4fnTX5.mjs.map +1 -0
  21. package/dist/{guest-auth-provider-Cc8y4Qd1.mjs → guest-auth-provider-DjOFjtnk.mjs} +10 -5
  22. package/dist/guest-auth-provider-DjOFjtnk.mjs.map +1 -0
  23. package/dist/{guest-auth-provider-D9FGNuP9.js → guest-auth-provider-Tndt6tzJ.js} +10 -5
  24. package/dist/guest-auth-provider-Tndt6tzJ.js.map +1 -0
  25. package/dist/{index-B9__gwTi.mjs → index-D3kdhCpH.mjs} +313 -84
  26. package/dist/index-D3kdhCpH.mjs.map +1 -0
  27. package/dist/{index-BghQ8jX0.js → index-DJeEMevo.js} +312 -83
  28. package/dist/index-DJeEMevo.js.map +1 -0
  29. package/dist/index.js +1 -1
  30. package/dist/index.mjs +1 -1
  31. package/dist/{index.native-BHjpXkKQ.mjs → index.native-CAH_ks0s.mjs} +200 -56
  32. package/dist/index.native-CAH_ks0s.mjs.map +1 -0
  33. package/dist/{index.native-B_AFa0GD.js → index.native-ingNhWPw.js} +199 -55
  34. package/dist/index.native-ingNhWPw.js.map +1 -0
  35. package/dist/index.native.js +1 -1
  36. package/dist/index.native.mjs +1 -1
  37. package/dist/{injected-evm-wallet-provider-CIeCSOPL.mjs → injected-evm-wallet-provider-BtyG1HOX.mjs} +4 -3
  38. package/dist/injected-evm-wallet-provider-BtyG1HOX.mjs.map +1 -0
  39. package/dist/{injected-evm-wallet-provider-DeLUwvGs.js → injected-evm-wallet-provider-Cao1U0NZ.js} +4 -3
  40. package/dist/{injected-evm-wallet-provider-DeLUwvGs.js.map → injected-evm-wallet-provider-Cao1U0NZ.js.map} +1 -1
  41. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs → injected-evm-wallet-provider-DXjwPjLW.mjs} +4 -3
  42. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs.map → injected-evm-wallet-provider-DXjwPjLW.mjs.map} +1 -1
  43. package/dist/{injected-evm-wallet-provider-Ckb85p6a.js → injected-evm-wallet-provider-IlkuKffc.js} +4 -3
  44. package/dist/injected-evm-wallet-provider-IlkuKffc.js.map +1 -0
  45. package/dist/{injected-wallet-provider-BlzA0J_V.mjs → injected-wallet-provider-0SSONh1d.mjs} +4 -3
  46. package/dist/{injected-wallet-provider-BlzA0J_V.mjs.map → injected-wallet-provider-0SSONh1d.mjs.map} +1 -1
  47. package/dist/{injected-wallet-provider-Cfb4ayCP.js → injected-wallet-provider-BEYwsn6M.js} +4 -3
  48. package/dist/{injected-wallet-provider-Cfb4ayCP.js.map → injected-wallet-provider-BEYwsn6M.js.map} +1 -1
  49. package/dist/{injected-wallet-provider-BEuj4sM3.mjs → injected-wallet-provider-CfYNIIHd.mjs} +4 -3
  50. package/dist/{injected-wallet-provider-BEuj4sM3.mjs.map → injected-wallet-provider-CfYNIIHd.mjs.map} +1 -1
  51. package/dist/{injected-wallet-provider-CUKdAuFu.js → injected-wallet-provider-DSlHfgMU.js} +4 -3
  52. package/dist/{injected-wallet-provider-CUKdAuFu.js.map → injected-wallet-provider-DSlHfgMU.js.map} +1 -1
  53. package/package.json +2 -2
  54. package/dist/email-auth-provider-BfBvF_4M.mjs.map +0 -1
  55. package/dist/email-auth-provider-CVp6hSCo.mjs.map +0 -1
  56. package/dist/email-auth-provider-CiqhQ1c2.js.map +0 -1
  57. package/dist/email-auth-provider-DxS2WYxM.js.map +0 -1
  58. package/dist/guest-auth-provider-Bm_yqQF6.mjs.map +0 -1
  59. package/dist/guest-auth-provider-Cc8y4Qd1.mjs.map +0 -1
  60. package/dist/guest-auth-provider-D9FGNuP9.js.map +0 -1
  61. package/dist/guest-auth-provider-DDBXeHoo.js.map +0 -1
  62. package/dist/index-B9__gwTi.mjs.map +0 -1
  63. package/dist/index-BghQ8jX0.js.map +0 -1
  64. package/dist/index.native-BHjpXkKQ.mjs.map +0 -1
  65. package/dist/index.native-B_AFa0GD.js.map +0 -1
  66. package/dist/injected-evm-wallet-provider-CIeCSOPL.mjs.map +0 -1
  67. package/dist/injected-evm-wallet-provider-Ckb85p6a.js.map +0 -1
@@ -236,8 +236,9 @@ class OffchainAuthProvider {
236
236
  async logout() {
237
237
  await this.wrappedProvider.logout();
238
238
  }
239
- async restoreSession() {
240
- const user = await this.wrappedProvider.restoreSession();
239
+ async restoreSession(beforeCommit) {
240
+ const provider = this.wrappedProvider;
241
+ const user = await provider.restoreSession(beforeCommit);
241
242
  if (user) {
242
243
  return Object.assign(Object.assign({}, user), { provider: this });
243
244
  }
@@ -5297,7 +5298,8 @@ async function loginWithRedirect(opts) {
5297
5298
  return runNativeLogin(opts);
5298
5299
  if (!hasDOM$1())
5299
5300
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
5300
- const { url, pkce } = await buildAuthorize(opts);
5301
+ const { url, pkce } = await untilAborted(buildAuthorize(opts), opts.signal);
5302
+ throwIfCancelled(opts.signal);
5301
5303
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
5302
5304
  // is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
5303
5305
  // this attempt's own state so a second concurrent login cannot overwrite it.
@@ -5326,9 +5328,29 @@ async function completeLoginFromRedirect() {
5326
5328
  throw error;
5327
5329
  return result.user;
5328
5330
  }
5331
+ function throwIfCancelled(signal) {
5332
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
5333
+ throw new Error('cancelled');
5334
+ }
5335
+ async function untilAborted(work, signal) {
5336
+ if (!signal)
5337
+ return work;
5338
+ throwIfCancelled(signal);
5339
+ let onAbort;
5340
+ try {
5341
+ return await Promise.race([work, new Promise((_, reject) => {
5342
+ onAbort = () => reject(new Error('cancelled'));
5343
+ signal.addEventListener('abort', onAbort, { once: true });
5344
+ })]);
5345
+ }
5346
+ finally {
5347
+ signal.removeEventListener('abort', onAbort);
5348
+ }
5349
+ }
5329
5350
  /** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
5330
5351
  * redirect_uri page initializes the SDK, which hands the code back automatically. */
5331
5352
  async function loginWithPopup(opts) {
5353
+ throwIfCancelled(opts.signal);
5332
5354
  if (!hasDOM$1())
5333
5355
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
5334
5356
  // Open synchronously, before PKCE/config awaits consume the click's user-
@@ -5346,7 +5368,7 @@ async function loginWithPopup(opts) {
5346
5368
  throw new Error('Popup blocked — use loginWithRedirect() instead');
5347
5369
  let authorization;
5348
5370
  try {
5349
- authorization = await buildAuthorize(opts, 'popup');
5371
+ authorization = await untilAborted(buildAuthorize(opts, 'popup'), opts.signal);
5350
5372
  }
5351
5373
  catch (error) {
5352
5374
  try {
@@ -5363,6 +5385,7 @@ async function loginWithPopup(opts) {
5363
5385
  // window with no listener or watchdog attached.
5364
5386
  let expectedOrigin;
5365
5387
  try {
5388
+ throwIfCancelled(opts.signal);
5366
5389
  expectedOrigin = new URL(pkce.redirectUri).origin;
5367
5390
  sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
5368
5391
  }
@@ -5394,20 +5417,42 @@ async function loginWithPopup(opts) {
5394
5417
  setAuthLoading(true); // reflect "signing in…" while the popup is open
5395
5418
  try {
5396
5419
  return await new Promise((resolve, reject) => {
5420
+ var _a, _b;
5397
5421
  let settled = false;
5422
+ let handedOff = false;
5423
+ let committing = false;
5398
5424
  let timer;
5399
5425
  let navigationTimer;
5400
5426
  let completionTimer;
5401
5427
  const cleanup = () => {
5402
- settled = true;
5403
5428
  clearInterval(timer);
5404
5429
  clearTimeout(navigationTimer);
5405
5430
  clearTimeout(completionTimer);
5406
5431
  window.removeEventListener('message', onMsg);
5407
5432
  removeAttempt(pkce.state);
5408
5433
  };
5434
+ const finish = (error, user) => {
5435
+ var _a;
5436
+ if (settled)
5437
+ return;
5438
+ settled = true;
5439
+ cleanup();
5440
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener('abort', onAbort);
5441
+ try {
5442
+ popup.close();
5443
+ }
5444
+ catch ( /* ignore */_b) { /* ignore */ }
5445
+ if (error)
5446
+ reject(error);
5447
+ else
5448
+ resolve(user);
5449
+ };
5450
+ // Provider adoption commits synchronously after its final guard. An auth
5451
+ // listener dismissing the card there cannot cancel an already signed-in user.
5452
+ const onAbort = () => { if (!committing)
5453
+ finish(new Error('cancelled')); };
5409
5454
  const onMsg = async (ev) => {
5410
- if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
5455
+ if (settled || handedOff || ev.origin !== expectedOrigin || ev.source !== popup)
5411
5456
  return;
5412
5457
  const d = ev.data;
5413
5458
  if (!d || d.type !== 'bounded-oidc-code' ||
@@ -5421,35 +5466,30 @@ async function loginWithPopup(opts) {
5421
5466
  return;
5422
5467
  if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
5423
5468
  return;
5469
+ handedOff = true;
5424
5470
  cleanup();
5425
5471
  try {
5472
+ throwIfCancelled(opts.signal);
5426
5473
  await validateAttempt(pkce, new URL(d.callbackUrl));
5474
+ throwIfCancelled(opts.signal);
5427
5475
  popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
5428
5476
  if (d.error)
5429
5477
  throw new Error(typeof d.errorDescription === 'string' ? d.errorDescription : `Login failed: ${d.error}`);
5430
- const user = await exchangeCode(d.code, pkce);
5431
- try {
5432
- popup.close();
5433
- }
5434
- catch ( /* ignore */_a) { /* ignore */ }
5435
- resolve(user);
5478
+ const user = await exchangeCode(d.code, pkce, opts.signal, () => { committing = true; });
5479
+ finish(undefined, user);
5436
5480
  }
5437
5481
  catch (e) {
5438
- try {
5439
- popup.close();
5440
- }
5441
- catch ( /* ignore */_b) { /* ignore */ }
5442
- reject(e);
5482
+ finish(e);
5443
5483
  }
5444
5484
  };
5485
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', onAbort, { once: true });
5486
+ if ((_b = opts.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
5487
+ onAbort();
5488
+ return;
5489
+ }
5445
5490
  window.addEventListener('message', onMsg);
5446
5491
  completionTimer = setTimeout(() => {
5447
- cleanup();
5448
- try {
5449
- popup.close();
5450
- }
5451
- catch ( /* ignore */_a) { /* ignore */ }
5452
- reject(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5492
+ finish(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5453
5493
  }, LOGIN_TIMEOUT_MS);
5454
5494
  // Some browser/extension combinations can return a real popup handle but
5455
5495
  // strand the window at about:blank instead of navigating to the hosted
@@ -5470,23 +5510,18 @@ async function loginWithPopup(opts) {
5470
5510
  const NAVIGATION_RECHECK_MS = 2000;
5471
5511
  const navigationStartedAt = Date.now();
5472
5512
  const checkNavigation = () => {
5473
- if (settled || popup.closed)
5513
+ if (settled || handedOff || popup.closed)
5474
5514
  return;
5475
5515
  try {
5476
5516
  if (popup.location.href === 'about:blank') {
5477
5517
  if (Date.now() - navigationStartedAt >= NAVIGATION_GRACE_MS) {
5478
- cleanup();
5479
- try {
5480
- popup.close();
5481
- }
5482
- catch ( /* ignore */_a) { /* ignore */ }
5483
- reject(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5518
+ finish(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5484
5519
  return;
5485
5520
  }
5486
5521
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5487
5522
  }
5488
5523
  }
5489
- catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
5524
+ catch ( /* cross-origin hosted auth loaded normally */_a) { /* cross-origin hosted auth loaded normally */ }
5490
5525
  };
5491
5526
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5492
5527
  // The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
@@ -5496,10 +5531,8 @@ async function loginWithPopup(opts) {
5496
5531
  timer = setInterval(() => {
5497
5532
  if (popup.closed) {
5498
5533
  clearInterval(timer);
5499
- setTimeout(() => { if (!settled) {
5500
- cleanup();
5501
- reject(new Error('Login window closed'));
5502
- } }, 500);
5534
+ setTimeout(() => { if (!settled && !handedOff)
5535
+ finish(new Error('Login window closed')); }, 500);
5503
5536
  }
5504
5537
  }, 500);
5505
5538
  });
@@ -5508,9 +5541,10 @@ async function loginWithPopup(opts) {
5508
5541
  setAuthLoading(false);
5509
5542
  }
5510
5543
  }
5511
- async function exchangeCode(code, pkce) {
5544
+ async function exchangeCode(code, pkce, signal, onCommitting) {
5512
5545
  const generation = getInitializationGeneration();
5513
5546
  const beforeCommit = () => {
5547
+ throwIfCancelled(signal);
5514
5548
  if (generation !== getInitializationGeneration())
5515
5549
  throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
5516
5550
  };
@@ -5526,7 +5560,7 @@ async function exchangeCode(code, pkce) {
5526
5560
  let res;
5527
5561
  try {
5528
5562
  res = await fetch(`${base}/api/auth/oauth2/token`, {
5529
- method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(),
5563
+ method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(), signal,
5530
5564
  });
5531
5565
  }
5532
5566
  catch (err) {
@@ -5552,7 +5586,7 @@ async function exchangeCode(code, pkce) {
5552
5586
  // Adopt on EVERY session surface (see establishInlineSession) - hosted
5553
5587
  // popup/redirect completions all land here, so this closes their gap too.
5554
5588
  try {
5555
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5589
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5556
5590
  }
5557
5591
  catch (error) {
5558
5592
  if (typeof rollback === 'function')
@@ -5621,6 +5655,31 @@ function idTokenIsGuest(idToken, expectedAddress) {
5621
5655
  function storedSessionIsGuest() {
5622
5656
  return idTokenIsGuest(getStoredIdToken());
5623
5657
  }
5658
+ /** Internal: select a cross-tab restore from bearer claims, never global hints. */
5659
+ function storedSessionAuthMethod(config) {
5660
+ try {
5661
+ const token = getStoredIdToken();
5662
+ if (!token)
5663
+ return null;
5664
+ let payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
5665
+ payload += '='.repeat((4 - payload.length % 4) % 4);
5666
+ const claims = JSON.parse(getPlatform().atob(payload));
5667
+ const method = claims.is_anonymous === true ? 'guest'
5668
+ : claims['custom:walletProvider'] === 'turnkey' || claims.email ? 'email'
5669
+ : claims['custom:evmWalletAddress'] ? 'evm-wallet'
5670
+ : claims['custom:walletAddress'] ? 'phantom' : 'email';
5671
+ if (config.authMethod != null && canonicalWalletAuthMethod(config.authMethod) !== method)
5672
+ return null;
5673
+ if (method === 'evm-wallet' && config.authMethod !== 'evm-wallet')
5674
+ return null;
5675
+ if (method === 'phantom' && (isWalletLoginDisabled(config) || isEmailRequired(config)))
5676
+ return null;
5677
+ return method;
5678
+ }
5679
+ catch (_a) {
5680
+ return null;
5681
+ }
5682
+ }
5624
5683
  /**
5625
5684
  * Pick the provider used for an ordinary `init({ appId })` restore.
5626
5685
  *
@@ -5867,9 +5926,10 @@ function optionalProviderError(method) {
5867
5926
  `loginWithRedirect(...) for Bounded Auth, signInAnonymously() for guests, ` +
5868
5927
  `or install/import an explicit wallet-provider entry once your app opts into "${method}".`);
5869
5928
  }
5870
- async function getAuthProvider(config) {
5929
+ async function getAuthProvider(config, fromStorage = false) {
5871
5930
  var _a;
5872
- const nextProviderConfigKey = config ? authProviderConfigKey(config) : null;
5931
+ const requestedAuthMethod = config ? (fromStorage ? storedSessionAuthMethod(config) : authMethodForInit(config)) : null;
5932
+ const nextProviderConfigKey = config ? authProviderConfigKey(Object.assign(Object.assign({}, config), { authMethod: requestedAuthMethod })) : null;
5873
5933
  if (currentAuthProvider && (!config || currentAuthProviderConfigKey === nextProviderConfigKey)) {
5874
5934
  return currentAuthProvider;
5875
5935
  }
@@ -5884,7 +5944,8 @@ async function getAuthProvider(config) {
5884
5944
  initConfig = config;
5885
5945
  // Keep what the app actually wrote for error text; everything else runs on
5886
5946
  // the canonical method so exactly one string reaches storage and the issuer.
5887
- const requestedAuthMethod = authMethodForInit(config);
5947
+ if (!requestedAuthMethod)
5948
+ throw new Error('The stored session is incompatible with this app authentication configuration.');
5888
5949
  const authMethod = canonicalWalletAuthMethod(requestedAuthMethod);
5889
5950
  const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
5890
5951
  const configuredNetwork = getConfiguredSolanaNetwork(config);
@@ -5909,7 +5970,7 @@ async function getAuthProvider(config) {
5909
5970
  // manager as legacy email sessions. Human login should be started
5910
5971
  // with loginWithRedirect(); this provider is kept as the restore and
5911
5972
  // logout surface for those sessions.
5912
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-CiqhQ1c2.js'); })).EmailAuthProvider();
5973
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-DTrCfOJd.js'); })).EmailAuthProvider();
5913
5974
  break;
5914
5975
  case "phantom": {
5915
5976
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -5951,7 +6012,7 @@ async function getAuthProvider(config) {
5951
6012
  // (bounded-login-widget.ts), so registration never counts as
5952
6013
  // evidence the user has a wallet.
5953
6014
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
5954
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CUKdAuFu.js'); });
6015
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-DSlHfgMU.js'); });
5955
6016
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
5956
6017
  break;
5957
6018
  }
@@ -5964,7 +6025,7 @@ async function getAuthProvider(config) {
5964
6025
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
5965
6026
  // to point at a specific wallet or set chainId/domain.
5966
6027
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
5967
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-DeLUwvGs.js'); });
6028
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-Cao1U0NZ.js'); });
5968
6029
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
5969
6030
  break;
5970
6031
  }
@@ -5989,7 +6050,7 @@ async function getAuthProvider(config) {
5989
6050
  break;
5990
6051
  case "guest":
5991
6052
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
5992
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-D9FGNuP9.js'); })).GuestAuthProvider();
6053
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Tndt6tzJ.js'); })).GuestAuthProvider();
5993
6054
  break;
5994
6055
  }
5995
6056
  if (!currentAuthProvider) {
@@ -6051,7 +6112,7 @@ async function signInAnonymously() {
6051
6112
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6052
6113
  // the main bundle for data-only apps. Specifier is a static string literal
6053
6114
  // so bundle-splitting can see it.
6054
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-D9FGNuP9.js'); })).GuestAuthProvider();
6115
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Tndt6tzJ.js'); })).GuestAuthProvider();
6055
6116
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6056
6117
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6057
6118
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6222,7 +6283,7 @@ function ensureSharedWalletLoginReady() {
6222
6283
  if (!sharedWalletReady) {
6223
6284
  sharedWalletReady = (async () => {
6224
6285
  await core.getConfig();
6225
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-CUKdAuFu.js'); });
6286
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-DSlHfgMU.js'); });
6226
6287
  })().catch((error) => {
6227
6288
  sharedWalletReady = null;
6228
6289
  throw error;
@@ -6518,12 +6579,80 @@ let isAuthLoading = false;
6518
6579
  let authReadyPromise = null;
6519
6580
  let initializingConfig = null;
6520
6581
  let initializationGeneration = 0;
6582
+ let initializedConfig = null;
6583
+ let storageBridgeRegistered = false;
6584
+ let observedIdToken = null;
6585
+ let observedIssuer = null;
6586
+ // Storage events carry no authority. Re-read the active manager's app/issuer
6587
+ // namespace, then use the ordinary initializer/provider restore and listeners.
6588
+ function registerStorageBridge() {
6589
+ if (storageBridgeRegistered || !getPlatform().hasDOM || typeof window === 'undefined')
6590
+ return;
6591
+ storageBridgeRegistered = true;
6592
+ window.addEventListener('storage', (event) => {
6593
+ if (event.storageArea !== window.localStorage || !initializedConfig)
6594
+ return;
6595
+ void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
6596
+ });
6597
+ }
6598
+ async function syncStoredSession() {
6599
+ const config = initializedConfig;
6600
+ if (!config)
6601
+ return;
6602
+ const token = core.WebSessionManager.getIdToken();
6603
+ const issuer = core.WebSessionManager.getIssuer();
6604
+ if (token === observedIdToken && issuer === observedIssuer)
6605
+ return;
6606
+ observedIdToken = token;
6607
+ observedIssuer = issuer;
6608
+ const generation = ++initializationGeneration;
6609
+ // Hide the former principal while the replacement is validated. This also
6610
+ // reconnects live reads through the existing auth-state change path.
6611
+ setCurrentUser(null);
6612
+ if (authReadyPromise)
6613
+ await authReadyPromise.catch(() => { });
6614
+ if (generation !== initializationGeneration || config !== initializedConfig)
6615
+ return;
6616
+ const record = await core.WebSessionManager.readSessionWithGeneration();
6617
+ if (generation !== initializationGeneration || config !== initializedConfig)
6618
+ return;
6619
+ if (!record)
6620
+ return;
6621
+ const normalize = (url) => (url || '').replace(/\/+$/, '');
6622
+ const resolvedConfig = await core.getConfig();
6623
+ if (generation !== initializationGeneration || config !== initializedConfig)
6624
+ return;
6625
+ const allowedIssuers = [normalize(resolvedConfig.humanAuthApiUrl || 'https://auth.bounded.sh'), normalize(resolvedConfig.authApiUrl)].filter(Boolean);
6626
+ // A legacy shared blob can belong to another app. Never restore or clear
6627
+ // that app's credential; the current app remains signed out.
6628
+ if ((record.appId && record.appId !== config.appId)
6629
+ || (record.issuer && !allowedIssuers.includes(normalize(record.issuer))))
6630
+ return;
6631
+ if (!storedSessionAuthMethod(config))
6632
+ return;
6633
+ const restored = startInitialization(config, true);
6634
+ const restoreGeneration = initializationGeneration;
6635
+ try {
6636
+ await restored;
6637
+ }
6638
+ catch (error) {
6639
+ if (restoreGeneration === initializationGeneration && core.WebSessionManager.getIdToken())
6640
+ throw error;
6641
+ }
6642
+ }
6521
6643
  /** Internal guard for a login result arriving after the app reconfigured the SDK. */
6522
6644
  function getInitializationGeneration() { return initializationGeneration; }
6523
6645
  // This file acts as a middleware for the global state of the SDK
6524
6646
  // This mostly involves setting up the AuthProvider and managing the current user
6525
- async function initialize(requestedConfig) {
6647
+ async function initialize(requestedConfig, fromStorage) {
6526
6648
  const newConfig = withDefaultSolanaRpcUrl(requestedConfig);
6649
+ const generation = initializationGeneration;
6650
+ const validateStorage = fromStorage ? core.WebSessionManager.captureSessionEstablishment() : undefined;
6651
+ const beforeCommit = () => {
6652
+ if (generation !== initializationGeneration)
6653
+ throw new Error('Session restore was superseded.');
6654
+ validateStorage === null || validateStorage === void 0 ? void 0 : validateStorage();
6655
+ };
6527
6656
  // Mark auth as loading for the ENTIRE init/restore window so consumers render
6528
6657
  // a neutral/loading state instead of the signed-out UI while we rehydrate the
6529
6658
  // session — otherwise getCurrentUser() is null + loading is false during the
@@ -6542,8 +6671,9 @@ async function initialize(requestedConfig) {
6542
6671
  // onAuthStateChanged never fired, and every consumer UI went on showing a
6543
6672
  // connected wallet for a session the server no longer knew about.
6544
6673
  registerSessionClearedBridge();
6674
+ registerStorageBridge();
6545
6675
  // Get auth provider first
6546
- authProviderInstance = await getAuthProvider(newConfig);
6676
+ authProviderInstance = await getAuthProvider(newConfig, fromStorage);
6547
6677
  // Initialize config with auth provider
6548
6678
  await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
6549
6679
  // A return establishes its own provider/session, or hands off a popup.
@@ -6568,7 +6698,8 @@ async function initialize(requestedConfig) {
6568
6698
  // Wait for restoreSession to complete
6569
6699
  // The appId check is done in SessionManager.getSession which is called by restoreSession
6570
6700
  // If there's an appId mismatch, the session will be cleared and restoreSession will return null
6571
- let user = await authProviderInstance.restoreSession();
6701
+ let user = await authProviderInstance.restoreSession(beforeCommit);
6702
+ beforeCommit();
6572
6703
  // If a session exists but was created before auth method tracking (legacy session),
6573
6704
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
6574
6705
  //
@@ -6576,9 +6707,10 @@ async function initialize(requestedConfig) {
6576
6707
  // unawaited call would return a Promise (always truthy) — nulling the user
6577
6708
  // unconditionally AND letting setCurrentUser run while the old credential
6578
6709
  // was still in storage.
6579
- if (user && await clearIncompatibleSession()) {
6710
+ if (user && !fromStorage && await clearIncompatibleSession()) {
6580
6711
  user = null;
6581
6712
  }
6713
+ beforeCommit();
6582
6714
  setCurrentUser(user);
6583
6715
  // Mark init as completed
6584
6716
  initCompleted = true;
@@ -6618,13 +6750,17 @@ function sameConfigValue(a, b, seen = new WeakMap()) {
6618
6750
  && sameConfigValue(a[key], b[key], seen));
6619
6751
  }
6620
6752
  function init(newConfig) {
6753
+ return startInitialization(newConfig, false);
6754
+ }
6755
+ function startInitialization(newConfig, fromStorage) {
6621
6756
  if (initializingConfig) {
6622
6757
  if (sameConfigValue(initializingConfig, newConfig)) {
6623
6758
  return authReadyPromise;
6624
6759
  }
6625
6760
  return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
6626
6761
  }
6627
- initializingConfig = Object.assign({}, newConfig);
6762
+ initializedConfig = Object.assign({}, newConfig);
6763
+ initializingConfig = initializedConfig;
6628
6764
  initializationGeneration++;
6629
6765
  let resolveReady;
6630
6766
  let rejectReady;
@@ -6636,7 +6772,7 @@ function init(newConfig) {
6636
6772
  // auth-loading listener to call waitForAuthReady() synchronously when init()
6637
6773
  // announces that session restoration has begun.
6638
6774
  authReadyPromise = ready;
6639
- void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6775
+ void initialize(initializingConfig, fromStorage).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6640
6776
  return ready;
6641
6777
  }
6642
6778
  /**
@@ -6709,6 +6845,13 @@ function userPrincipalKey(user) {
6709
6845
  }
6710
6846
  // Update the currentUser and notify listeners
6711
6847
  function setCurrentUser(user) {
6848
+ observedIdToken = getCurrentIdToken();
6849
+ try {
6850
+ observedIssuer = core.getActiveSessionManager().getIssuer();
6851
+ }
6852
+ catch (_a) {
6853
+ observedIssuer = null;
6854
+ }
6712
6855
  // Enrich with the universal identity fields (id/email) derived from the
6713
6856
  // active session's idToken — central chokepoint so every provider's user
6714
6857
  // object gains { id, address, email } without each construction site
@@ -8511,8 +8654,9 @@ class PrivyExpoProvider {
8511
8654
  const { signature } = await this.signAndSubmitInternal(transaction, wallet, connection);
8512
8655
  return signature;
8513
8656
  }
8514
- async restoreSession() {
8657
+ async restoreSession(beforeCommit) {
8515
8658
  const session = await core.getActiveSessionManager().getSession();
8659
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
8516
8660
  if (session) {
8517
8661
  this.authenticatedAddress = session.address;
8518
8662
  return { provider: this, address: session.address };
@@ -8674,4 +8818,4 @@ exports.useAuth = useAuth;
8674
8818
  exports.useBuildRun = useBuildRun;
8675
8819
  exports.useQuery = useQuery;
8676
8820
  exports.waitForAuthReady = waitForAuthReady;
8677
- //# sourceMappingURL=index.native-B_AFa0GD.js.map
8821
+ //# sourceMappingURL=index.native-ingNhWPw.js.map