@bounded-sh/client 0.0.41 → 0.0.43
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/REACT_NATIVE.md +12 -14
- package/dist/index.js +85 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -7
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +79 -7
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +79 -7
- package/dist/index.native.mjs.map +1 -1
- package/package.json +128 -117
package/REACT_NATIVE.md
CHANGED
|
@@ -63,20 +63,18 @@ On native, `loginWithRedirect()` opens the system/in-app browser and resolves
|
|
|
63
63
|
with the user when the callback returns to the app. `completeLoginFromRedirect()`
|
|
64
64
|
is for web callback pages.
|
|
65
65
|
|
|
66
|
-
## 3. Guest login
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
The device-local ed25519 keypair is stored in the RN storage adapter, so the
|
|
79
|
-
guest identity survives app restarts until `logout()` clears it.
|
|
66
|
+
## 3. Guest login is browser-only in the current release
|
|
67
|
+
|
|
68
|
+
Do not call `signInAnonymously()` from React Native. The current guest identity
|
|
69
|
+
uses a non-extractable WebCrypto Ed25519 key stored in IndexedDB; it does not use
|
|
70
|
+
the React Native session-storage adapter above. Native runtimes therefore fail
|
|
71
|
+
closed before making an authentication request.
|
|
72
|
+
|
|
73
|
+
Use hosted email/social login from section 2, or the explicit Privy Expo bridge
|
|
74
|
+
below. Do not add an IndexedDB polyfill to persist a guest signing key: that does
|
|
75
|
+
not provide the platform-backed, non-exportable key contract a native guest flow
|
|
76
|
+
needs. Native anonymous auth will be documented here only after the SDK ships a
|
|
77
|
+
Keychain/Keystore-backed signing implementation and migration/loss semantics.
|
|
80
78
|
|
|
81
79
|
## 4. Privy on React Native (`privy-expo`)
|
|
82
80
|
|
package/dist/index.js
CHANGED
|
@@ -4530,23 +4530,74 @@ async function completeLoginFromRedirect() {
|
|
|
4530
4530
|
async function loginWithPopup(opts) {
|
|
4531
4531
|
if (!hasDOM())
|
|
4532
4532
|
throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
|
|
4533
|
-
|
|
4534
|
-
|
|
4533
|
+
// Open synchronously, before PKCE/config awaits consume the click's user-
|
|
4534
|
+
// activation window. Opening only after buildAuthorize() intermittently
|
|
4535
|
+
// left Chrome with a real named window stranded at about:blank (or blocked
|
|
4536
|
+
// it entirely), while callers waited forever for an OAuth message.
|
|
4535
4537
|
const w = opts.width || 480, h = opts.height || 720;
|
|
4536
4538
|
const left = window.screenX + (window.outerWidth - w) / 2;
|
|
4537
4539
|
const top = window.screenY + (window.outerHeight - h) / 2;
|
|
4538
|
-
const popup = window.open(
|
|
4540
|
+
const popup = window.open('', 'bounded-oidc', `width=${w},height=${h},left=${left},top=${top}`);
|
|
4539
4541
|
if (!popup)
|
|
4540
4542
|
throw new Error('Popup blocked — use loginWithRedirect() instead');
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4543
|
+
let authorization;
|
|
4544
|
+
try {
|
|
4545
|
+
authorization = await buildAuthorize(opts);
|
|
4546
|
+
}
|
|
4547
|
+
catch (error) {
|
|
4548
|
+
try {
|
|
4549
|
+
popup.close();
|
|
4550
|
+
}
|
|
4551
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
4552
|
+
throw error;
|
|
4553
|
+
}
|
|
4554
|
+
const { url, pkce } = authorization;
|
|
4555
|
+
// Validate every remaining synchronous setup step before navigating. Both
|
|
4556
|
+
// URL parsing and sessionStorage can throw (malformed caller input, blocked
|
|
4557
|
+
// storage, quota/privacy mode); because the popup is already open to retain
|
|
4558
|
+
// user activation, close it on either failure instead of orphaning a blank
|
|
4559
|
+
// window with no listener or watchdog attached.
|
|
4560
|
+
let expectedOrigin;
|
|
4561
|
+
try {
|
|
4562
|
+
expectedOrigin = new URL(pkce.redirectUri).origin;
|
|
4563
|
+
sessionStorage.setItem(PKCE_STORE_KEY, JSON.stringify(pkce));
|
|
4564
|
+
}
|
|
4565
|
+
catch (error) {
|
|
4566
|
+
try {
|
|
4567
|
+
popup.close();
|
|
4568
|
+
}
|
|
4569
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
4570
|
+
throw error;
|
|
4571
|
+
}
|
|
4572
|
+
try {
|
|
4573
|
+
popup.location.replace(url);
|
|
4574
|
+
}
|
|
4575
|
+
catch (_c) {
|
|
4576
|
+
try {
|
|
4577
|
+
popup.location.href = url;
|
|
4578
|
+
}
|
|
4579
|
+
catch (_d) {
|
|
4580
|
+
try {
|
|
4581
|
+
popup.close();
|
|
4582
|
+
}
|
|
4583
|
+
catch ( /* ignore */_e) { /* ignore */ }
|
|
4584
|
+
throw new Error('Popup failed to navigate — use loginWithRedirect() instead');
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
// expectedOrigin came from the RESOLVED redirect_uri (buildAuthorize defaulted it to the
|
|
4588
|
+
// current page on web), not opts.redirectUri which may be undefined.
|
|
4544
4589
|
setAuthLoading(true); // reflect "signing in…" while the popup is open
|
|
4545
4590
|
try {
|
|
4546
4591
|
return await new Promise((resolve, reject) => {
|
|
4547
4592
|
let settled = false;
|
|
4548
4593
|
let timer;
|
|
4549
|
-
|
|
4594
|
+
let navigationTimer;
|
|
4595
|
+
const cleanup = () => {
|
|
4596
|
+
settled = true;
|
|
4597
|
+
clearInterval(timer);
|
|
4598
|
+
clearTimeout(navigationTimer);
|
|
4599
|
+
window.removeEventListener('message', onMsg);
|
|
4600
|
+
};
|
|
4550
4601
|
const onMsg = async (ev) => {
|
|
4551
4602
|
if (ev.origin !== expectedOrigin)
|
|
4552
4603
|
return; // only trust the redirect_uri origin
|
|
@@ -4574,6 +4625,27 @@ async function loginWithPopup(opts) {
|
|
|
4574
4625
|
}
|
|
4575
4626
|
};
|
|
4576
4627
|
window.addEventListener('message', onMsg);
|
|
4628
|
+
// Some browser/extension combinations can return a real popup handle but
|
|
4629
|
+
// strand the window at about:blank instead of navigating to the hosted
|
|
4630
|
+
// authorize URL. The old code then waited forever because the popup was
|
|
4631
|
+
// open and no message could ever arrive. Once the popup becomes
|
|
4632
|
+
// cross-origin, reading location throws — that is positive evidence that
|
|
4633
|
+
// navigation succeeded, so only a still-readable about:blank is rejected.
|
|
4634
|
+
navigationTimer = setTimeout(() => {
|
|
4635
|
+
if (settled || popup.closed)
|
|
4636
|
+
return;
|
|
4637
|
+
try {
|
|
4638
|
+
if (popup.location.href === 'about:blank') {
|
|
4639
|
+
cleanup();
|
|
4640
|
+
try {
|
|
4641
|
+
popup.close();
|
|
4642
|
+
}
|
|
4643
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
4644
|
+
reject(new Error('Popup stalled at about:blank — use loginWithRedirect() instead'));
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
|
|
4648
|
+
}, 4000);
|
|
4577
4649
|
// The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
|
|
4578
4650
|
// races the just-posted message and would report a spurious "Login window closed" on a
|
|
4579
4651
|
// SUCCESSFUL login. When we see it closed, wait a beat for that pending message to settle
|
|
@@ -5345,6 +5417,12 @@ function useQuery(path, options) {
|
|
|
5345
5417
|
core.subscribe(path, Object.assign(Object.assign({}, options), { onData: (d) => {
|
|
5346
5418
|
if (!active)
|
|
5347
5419
|
return;
|
|
5420
|
+
// A reconnect can legitimately surface a transport error before the
|
|
5421
|
+
// socket re-authenticates and sends a new authorized snapshot. That
|
|
5422
|
+
// fresh snapshot is the recovery boundary: keep the error visible
|
|
5423
|
+
// until data arrives, then clear it so React does not remain stuck on
|
|
5424
|
+
// a failure page after realtime has recovered.
|
|
5425
|
+
setError(null);
|
|
5348
5426
|
setData(d);
|
|
5349
5427
|
setLoading(false);
|
|
5350
5428
|
}, onError: (e) => {
|