@oxyhq/services 8.1.2 → 8.2.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/lib/commonjs/ui/components/OxyProvider.js +10 -3
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +334 -79
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +7 -3
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +10 -3
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +335 -79
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +7 -3
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/ui/components/OxyProvider.tsx +4 -3
- package/src/ui/context/OxyContext.tsx +334 -90
- package/src/ui/context/hooks/useAuthOperations.ts +1 -1
- package/src/ui/hooks/useSessionManagement.ts +8 -4
|
@@ -10,6 +10,7 @@ var _OxyContext = require("../context/OxyContext.js");
|
|
|
10
10
|
var _reactQuery = require("@tanstack/react-query");
|
|
11
11
|
var _bloom = require("@oxyhq/bloom");
|
|
12
12
|
var _toast = require("@oxyhq/bloom/toast");
|
|
13
|
+
var _core = require("@oxyhq/core");
|
|
13
14
|
var _FontLoader = require("./FontLoader.js");
|
|
14
15
|
var _queryClient = require("../hooks/queryClient.js");
|
|
15
16
|
var _storageHelpers = require("../utils/storageHelpers.js");
|
|
@@ -53,7 +54,9 @@ const LazyBottomSheetRouter = /*#__PURE__*/(0, _react.lazy)(() => Promise.resolv
|
|
|
53
54
|
default: mod.default
|
|
54
55
|
}), error => {
|
|
55
56
|
if (__DEV__) {
|
|
56
|
-
|
|
57
|
+
_core.logger.error('Failed to load BottomSheetRouter', error instanceof Error ? error : new Error(String(error)), {
|
|
58
|
+
component: 'OxyProvider'
|
|
59
|
+
});
|
|
57
60
|
}
|
|
58
61
|
return {
|
|
59
62
|
default: () => null
|
|
@@ -118,7 +121,9 @@ const OxyProvider = ({
|
|
|
118
121
|
const moduleName = KEYBOARD_CONTROLLER_MODULE;
|
|
119
122
|
(specifier => new Promise(r => r(`${specifier}`)).then(s => _interopRequireWildcard(require(s))))(moduleName).then(mod => setKBProvider(() => mod.KeyboardProvider)).catch(error => {
|
|
120
123
|
if (__DEV__) {
|
|
121
|
-
|
|
124
|
+
_core.logger.warn('react-native-keyboard-controller not available, skipping keyboard support', {
|
|
125
|
+
component: 'OxyProvider'
|
|
126
|
+
}, error);
|
|
122
127
|
}
|
|
123
128
|
});
|
|
124
129
|
}, []);
|
|
@@ -161,7 +166,9 @@ const OxyProvider = ({
|
|
|
161
166
|
storage = await (0, _storageHelpers.createPlatformStorage)();
|
|
162
167
|
} catch (error) {
|
|
163
168
|
if (__DEV__) {
|
|
164
|
-
|
|
169
|
+
_core.logger.warn('Failed to initialize storage for query persistence', {
|
|
170
|
+
component: 'OxyProvider'
|
|
171
|
+
}, error);
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
174
|
if (!mounted || queryClientRef.current) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","_reactNative","_OxyContext","_reactQuery","_bloom","_toast","_FontLoader","_queryClient","_storageHelpers","_jsxRuntime","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","BOOT_BG_COLOR","bootStyles","StyleSheet","create","bootShell","flex","backgroundColor","setupFonts","isWeb","Platform","OS","KEYBOARD_CONTROLLER_MODULE","LazyBottomSheetRouter","lazy","Promise","resolve","then","mod","error","__DEV__","
|
|
1
|
+
{"version":3,"names":["_react","require","_reactNative","_OxyContext","_reactQuery","_bloom","_toast","_core","_FontLoader","_queryClient","_storageHelpers","_jsxRuntime","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","BOOT_BG_COLOR","bootStyles","StyleSheet","create","bootShell","flex","backgroundColor","setupFonts","isWeb","Platform","OS","KEYBOARD_CONTROLLER_MODULE","LazyBottomSheetRouter","lazy","Promise","resolve","then","mod","error","__DEV__","loggerUtil","Error","String","component","LazySignInModal","OxyProvider","oxyServices","children","onAuthStateChange","storageKeyPrefix","baseURL","authWebUrl","authRedirectUri","queryClient","providedQueryClient","KBProvider","setKBProvider","useState","useEffect","moduleName","specifier","s","KeyboardProvider","catch","warn","KeyboardWrapper","jsx","Fragment","storageRef","useRef","queryClientRef","persistenceUnsubRef","setQueryClient","current","mounted","bootstrap","storage","createPlatformStorage","client","createQueryClient","restored","unsubscribe","attachQueryPersistence","handleVisibilityChange","focusManager","setFocused","document","visibilityState","addEventListener","removeEventListener","subscription","AppState","state","remove","cleanup","setupNetworkMonitoring","onlineManager","setOnline","navigator","onLine","handleOnline","handleOffline","window","NetInfo","fetch","isConnected","View","style","coreContent","QueryClientProvider","BloomDialogProvider","jsxs","OxyContextProvider","Suspense","fallback","ToastOutlet","_default","exports"],"sourceRoot":"../../../../src","sources":["ui/components/OxyProvider.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -30,6 +30,71 @@ const OxyContext = /*#__PURE__*/(0, _react.createContext)(null);
|
|
|
30
30
|
// `../utils/activeAuthuser` so the session-management and auth-operations hooks
|
|
31
31
|
// can share them without re-importing this 1k-line context file.
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Module-level run-once guard for the cold-boot silent SSO steps
|
|
35
|
+
* (`fedcm-silent` and `silent-iframe`).
|
|
36
|
+
*
|
|
37
|
+
* Both steps trigger a one-shot browser credential / iframe handshake that must
|
|
38
|
+
* fire AT MOST ONCE per page load — otherwise a provider remount storm (route
|
|
39
|
+
* churn, StrictMode double-invoke, error-boundary recovery) becomes a credential
|
|
40
|
+
* request storm. A per-instance ref resets on every remount, so the guard must
|
|
41
|
+
* live at module scope. Keyed on `origin|baseURL` so two providers pointed at
|
|
42
|
+
* the same API from the same origin share one attempt; never cleared because
|
|
43
|
+
* only a fresh page load can change the IdP session state, and a fresh page load
|
|
44
|
+
* starts a fresh module scope.
|
|
45
|
+
*
|
|
46
|
+
* This is a NEW, dedicated set — distinct from `useWebSSO`'s `silentSSOAttempted`
|
|
47
|
+
* (which guards the post-boot INTERACTIVE button path) and never a core
|
|
48
|
+
* module-level singleton (that re-evaluates under Metro web bundling and the
|
|
49
|
+
* guard would not hold).
|
|
50
|
+
*/
|
|
51
|
+
const servicesSilentAttempted = new Set();
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Build the `origin|baseURL` signature used as the silent-cold-boot guard key.
|
|
55
|
+
*/
|
|
56
|
+
function silentColdBootKey(oxyServices) {
|
|
57
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : 'no-origin';
|
|
58
|
+
let baseURL = '';
|
|
59
|
+
try {
|
|
60
|
+
baseURL = oxyServices.getBaseURL?.() ?? '';
|
|
61
|
+
} catch {
|
|
62
|
+
baseURL = '';
|
|
63
|
+
}
|
|
64
|
+
return `${origin}|${baseURL}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether `idpOrigin` is a same-site, first-party host of the current page —
|
|
69
|
+
* i.e. it shares the page's registrable apex (last two labels), so a "no
|
|
70
|
+
* session" answer from its `/auth/session-check` iframe is authoritative for
|
|
71
|
+
* THIS app and may force a local sign-out.
|
|
72
|
+
*
|
|
73
|
+
* On a cross-site IdP (or any host whose relationship to the page can't be
|
|
74
|
+
* positively established) this returns `false`, so the visibility-driven check
|
|
75
|
+
* may surface a session-ended toast but MUST NOT clear local state — a
|
|
76
|
+
* third-party / undetermined IdP answer can never force logout. Returns `false`
|
|
77
|
+
* off-browser.
|
|
78
|
+
*/
|
|
79
|
+
function isSameSiteIdP(idpOrigin) {
|
|
80
|
+
if (typeof window === 'undefined') return false;
|
|
81
|
+
let idpHostname;
|
|
82
|
+
try {
|
|
83
|
+
idpHostname = new URL(idpOrigin).hostname;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const pageHostname = window.location.hostname;
|
|
88
|
+
if (!idpHostname || !pageHostname) return false;
|
|
89
|
+
if (idpHostname === pageHostname) return true;
|
|
90
|
+
const apexOf = hostname => hostname.split('.').slice(-2).join('.');
|
|
91
|
+
const pageApex = apexOf(pageHostname);
|
|
92
|
+
// Require a real registrable apex (≥2 labels) AND an exact apex match AND that
|
|
93
|
+
// the IdP host is the page apex itself or a subdomain of it.
|
|
94
|
+
if (pageHostname.split('.').length < 2) return false;
|
|
95
|
+
if (apexOf(idpHostname) !== pageApex) return false;
|
|
96
|
+
return idpHostname === pageApex || idpHostname.endsWith(`.${pageApex}`);
|
|
97
|
+
}
|
|
33
98
|
let cachedUseFollowHook = null;
|
|
34
99
|
const loadUseFollowHook = () => {
|
|
35
100
|
if (cachedUseFollowHook) {
|
|
@@ -71,9 +136,19 @@ const OxyProvider = ({
|
|
|
71
136
|
if (providedOxyServices) {
|
|
72
137
|
oxyServicesRef.current = providedOxyServices;
|
|
73
138
|
} else if (baseURL) {
|
|
139
|
+
// Auto-detect the FAPI (IdP) origin from the current browser hostname so
|
|
140
|
+
// a consuming RP (mention.earth, homiio.com, alia.onl, …) targets
|
|
141
|
+
// `auth.<rp-apex>` for FedCM + the silent iframe WITHOUT passing
|
|
142
|
+
// `authWebUrl` explicitly — that is what makes both the FedCM config and
|
|
143
|
+
// the `/auth/silent` iframe first-party with the RP (Safari ITP /
|
|
144
|
+
// Firefox TCP need first-party). An explicit `authWebUrl` prop still
|
|
145
|
+
// wins. On native `autoDetectAuthWebUrl()` returns `undefined`
|
|
146
|
+
// (off-browser), leaving the value unchanged. We only auto-detect on the
|
|
147
|
+
// baseURL-only path — a consumer-provided `OxyServices` instance is
|
|
148
|
+
// never mutated.
|
|
74
149
|
oxyServicesRef.current = new _core.OxyServices({
|
|
75
150
|
baseURL,
|
|
76
|
-
authWebUrl,
|
|
151
|
+
authWebUrl: authWebUrl ?? (0, _core.autoDetectAuthWebUrl)(),
|
|
77
152
|
authRedirectUri
|
|
78
153
|
});
|
|
79
154
|
} else {
|
|
@@ -138,7 +213,9 @@ const OxyProvider = ({
|
|
|
138
213
|
}, [oxyServices]);
|
|
139
214
|
const logger = (0, _react.useCallback)((message, err) => {
|
|
140
215
|
if (__DEV__) {
|
|
141
|
-
|
|
216
|
+
_core.logger.warn(message, {
|
|
217
|
+
component: 'OxyContext'
|
|
218
|
+
}, err);
|
|
142
219
|
}
|
|
143
220
|
}, []);
|
|
144
221
|
const storageKeys = (0, _react.useMemo)(() => (0, _storageHelpers.getStorageKeys)(storageKeyPrefix), [storageKeyPrefix]);
|
|
@@ -357,6 +434,14 @@ const OxyProvider = ({
|
|
|
357
434
|
const onAuthStateChangeRef = (0, _react.useRef)(onAuthStateChange);
|
|
358
435
|
onAuthStateChangeRef.current = onAuthStateChange;
|
|
359
436
|
|
|
437
|
+
// `handleWebSSOSession` is declared further down (it depends on values that
|
|
438
|
+
// are only available there). The FedCM/iframe cold-boot steps need to commit
|
|
439
|
+
// a recovered session through it, so we route the call through a ref that is
|
|
440
|
+
// populated once the callback exists. The ref is assigned synchronously on
|
|
441
|
+
// every render before the cold-boot effect can fire (the effect is gated on
|
|
442
|
+
// `storage` + `initialized`, both of which settle after first render).
|
|
443
|
+
const handleWebSSOSessionRef = (0, _react.useRef)(null);
|
|
444
|
+
|
|
360
445
|
// Cold-boot session restore via the secure refresh cookies (web only).
|
|
361
446
|
//
|
|
362
447
|
// Calls `oxyServices.refreshAllSessions()` → `POST /auth/refresh-all` with
|
|
@@ -451,90 +536,232 @@ const OxyProvider = ({
|
|
|
451
536
|
onAuthStateChangeRef.current?.(fullUser);
|
|
452
537
|
return true;
|
|
453
538
|
}, [oxyServices, persistSessionDurably]);
|
|
539
|
+
|
|
540
|
+
// Native (and offline) stored-session restore — the ONLY restore path that
|
|
541
|
+
// runs on React Native, and the web fallback when no cross-domain step won.
|
|
542
|
+
//
|
|
543
|
+
// Verbatim-extracted from the previous `restoreSessionsFromStorage` body: it
|
|
544
|
+
// reads the durable `session_ids` / `active_session_id` slots, validates each
|
|
545
|
+
// stored session in parallel (bearer `validateSession`), and switches to the
|
|
546
|
+
// stored active session via the session-management `switchSession`. This body
|
|
547
|
+
// is platform-agnostic and gated by NO `enabled()` predicate so it runs on
|
|
548
|
+
// every platform — on native it is reached unconditionally (every web-only
|
|
549
|
+
// step ahead of it is disabled by `isWebBrowser()`), so native restore is
|
|
550
|
+
// exactly this and nothing else (no FedCM / iframe / refresh-all /
|
|
551
|
+
// handleAuthCallback).
|
|
552
|
+
const restoreStoredSession = (0, _react.useCallback)(async () => {
|
|
553
|
+
if (!storage) {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
|
|
557
|
+
const storedSessionIds = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
|
|
558
|
+
const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
|
|
559
|
+
let validSessions = [];
|
|
560
|
+
if (storedSessionIds.length > 0) {
|
|
561
|
+
// Validate all sessions in parallel (with 8s timeout per session) to avoid
|
|
562
|
+
// sequential blocking that freezes the app on startup
|
|
563
|
+
const VALIDATION_TIMEOUT = 8000;
|
|
564
|
+
const results = await Promise.allSettled(storedSessionIds.map(async sessionId => {
|
|
565
|
+
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(null), VALIDATION_TIMEOUT));
|
|
566
|
+
const validationPromise = oxyServices.validateSession(sessionId, {
|
|
567
|
+
useHeaderValidation: true
|
|
568
|
+
}).catch(validationError => {
|
|
569
|
+
if (!(0, _errorHandlers.isInvalidSessionError)(validationError) && !(0, _errorHandlers.isTimeoutOrNetworkError)(validationError)) {
|
|
570
|
+
logger('Session validation failed during init', validationError);
|
|
571
|
+
} else if (__DEV__ && (0, _errorHandlers.isTimeoutOrNetworkError)(validationError)) {
|
|
572
|
+
_core.logger.debug('Session validation timeout (expected when offline)', {
|
|
573
|
+
component: 'OxyContext',
|
|
574
|
+
method: 'restoreStoredSession'
|
|
575
|
+
}, validationError);
|
|
576
|
+
}
|
|
577
|
+
return null;
|
|
578
|
+
});
|
|
579
|
+
return Promise.race([validationPromise, timeoutPromise]).then(validation => {
|
|
580
|
+
if (validation?.valid && validation.user) {
|
|
581
|
+
const now = new Date();
|
|
582
|
+
return {
|
|
583
|
+
sessionId,
|
|
584
|
+
deviceId: '',
|
|
585
|
+
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
586
|
+
lastActive: now.toISOString(),
|
|
587
|
+
userId: validation.user.id?.toString() ?? '',
|
|
588
|
+
isCurrent: sessionId === storedActiveSessionId
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return null;
|
|
592
|
+
});
|
|
593
|
+
}));
|
|
594
|
+
validSessions = results.filter(r => r.status === 'fulfilled').map(r => r.value).filter(s => s !== null);
|
|
595
|
+
|
|
596
|
+
// Always persist validated sessions to storage (even empty list)
|
|
597
|
+
// to clear stale/expired session IDs that would cause 401 loops on restart
|
|
598
|
+
updateSessionsRef.current(validSessions, {
|
|
599
|
+
merge: false
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
if (storedActiveSessionId) {
|
|
603
|
+
try {
|
|
604
|
+
await switchSessionRef.current(storedActiveSessionId);
|
|
605
|
+
return true;
|
|
606
|
+
} catch (switchError) {
|
|
607
|
+
// Silently handle expected errors (invalid sessions, timeouts, network issues)
|
|
608
|
+
if ((0, _errorHandlers.isInvalidSessionError)(switchError)) {
|
|
609
|
+
await storage.removeItem(storageKeys.activeSessionId);
|
|
610
|
+
updateSessionsRef.current(validSessions.filter(session => session.sessionId !== storedActiveSessionId), {
|
|
611
|
+
merge: false
|
|
612
|
+
});
|
|
613
|
+
// Don't log expected session errors during restoration
|
|
614
|
+
} else if ((0, _errorHandlers.isTimeoutOrNetworkError)(switchError)) {
|
|
615
|
+
// Timeout/network error - non-critical, don't block
|
|
616
|
+
if (__DEV__) {
|
|
617
|
+
_core.logger.debug('Active session validation timeout (expected when offline)', {
|
|
618
|
+
component: 'OxyContext',
|
|
619
|
+
method: 'restoreStoredSession'
|
|
620
|
+
}, switchError);
|
|
621
|
+
}
|
|
622
|
+
} else {
|
|
623
|
+
// Only log unexpected errors
|
|
624
|
+
logger('Active session validation error', switchError);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return false;
|
|
629
|
+
}, [logger, oxyServices, storage, storageKeys.activeSessionId, storageKeys.sessionIds]);
|
|
630
|
+
|
|
631
|
+
// Cold boot — the single, ordered, short-circuit session-recovery sequence,
|
|
632
|
+
// consuming the SAME `runColdBoot` core primitive as `WebOxyProvider`. The
|
|
633
|
+
// FIRST step that yields a session wins; every later step is skipped. Each
|
|
634
|
+
// web-only step is gated by `isWebBrowser()`, so on native ONLY
|
|
635
|
+
// `stored-session` runs.
|
|
636
|
+
//
|
|
637
|
+
// Order (web): redirect callback → FedCM silent → silent iframe → refresh
|
|
638
|
+
// cookie → stored session. Order (native): stored session only.
|
|
454
639
|
const restoreSessionsFromStorage = (0, _react.useCallback)(async () => {
|
|
455
640
|
if (!storage) {
|
|
456
641
|
return;
|
|
457
642
|
}
|
|
458
643
|
setTokenReady(false);
|
|
644
|
+
const commitWebSession = handleWebSSOSessionRef.current;
|
|
645
|
+
const silentKey = silentColdBootKey(oxyServices);
|
|
646
|
+
const fedcmSupported = (0, _useWebSSO.isWebBrowser)() && oxyServices.isFedCMSupported?.() === true;
|
|
459
647
|
try {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
// Validate all sessions in parallel (with 8s timeout per session) to avoid
|
|
474
|
-
// sequential blocking that freezes the app on startup
|
|
475
|
-
const VALIDATION_TIMEOUT = 8000;
|
|
476
|
-
const results = await Promise.allSettled(storedSessionIds.map(async sessionId => {
|
|
477
|
-
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(null), VALIDATION_TIMEOUT));
|
|
478
|
-
const validationPromise = oxyServices.validateSession(sessionId, {
|
|
479
|
-
useHeaderValidation: true
|
|
480
|
-
}).catch(validationError => {
|
|
481
|
-
if (!(0, _errorHandlers.isInvalidSessionError)(validationError) && !(0, _errorHandlers.isTimeoutOrNetworkError)(validationError)) {
|
|
482
|
-
logger('Session validation failed during init', validationError);
|
|
483
|
-
} else if (__DEV__ && (0, _errorHandlers.isTimeoutOrNetworkError)(validationError)) {
|
|
484
|
-
_core.logger.debug('Session validation timeout (expected when offline)', {
|
|
485
|
-
component: 'OxyContext',
|
|
486
|
-
method: 'restoreSessionsFromStorage'
|
|
487
|
-
}, validationError);
|
|
488
|
-
}
|
|
489
|
-
return null;
|
|
490
|
-
});
|
|
491
|
-
return Promise.race([validationPromise, timeoutPromise]).then(validation => {
|
|
492
|
-
if (validation?.valid && validation.user) {
|
|
493
|
-
const now = new Date();
|
|
648
|
+
const outcome = await (0, _core.runColdBoot)({
|
|
649
|
+
steps: [{
|
|
650
|
+
// 1) Redirect callback wins: a popup/redirect sign-in just landed
|
|
651
|
+
// back on this page with `access_token`/`session_id` query params.
|
|
652
|
+
// `handleAuthCallback` plants the token but returns a PLACEHOLDER
|
|
653
|
+
// user (empty id), so we hydrate the REAL user via `getCurrentUser`
|
|
654
|
+
// and commit through `handleWebSSOSession` before claiming a
|
|
655
|
+
// session — never expose a placeholder user (R4).
|
|
656
|
+
id: 'redirect',
|
|
657
|
+
enabled: () => (0, _useWebSSO.isWebBrowser)(),
|
|
658
|
+
run: async () => {
|
|
659
|
+
const callbackSession = oxyServices.handleAuthCallback?.();
|
|
660
|
+
if (!callbackSession || !commitWebSession) {
|
|
494
661
|
return {
|
|
495
|
-
|
|
496
|
-
deviceId: '',
|
|
497
|
-
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
498
|
-
lastActive: now.toISOString(),
|
|
499
|
-
userId: validation.user.id?.toString() ?? '',
|
|
500
|
-
isCurrent: sessionId === storedActiveSessionId
|
|
662
|
+
kind: 'skip'
|
|
501
663
|
};
|
|
502
664
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
// Always persist validated sessions to storage (even empty list)
|
|
509
|
-
// to clear stale/expired session IDs that would cause 401 loops on restart
|
|
510
|
-
updateSessionsRef.current(validSessions, {
|
|
511
|
-
merge: false
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
if (storedActiveSessionId) {
|
|
515
|
-
try {
|
|
516
|
-
await switchSessionRef.current(storedActiveSessionId);
|
|
517
|
-
} catch (switchError) {
|
|
518
|
-
// Silently handle expected errors (invalid sessions, timeouts, network issues)
|
|
519
|
-
if ((0, _errorHandlers.isInvalidSessionError)(switchError)) {
|
|
520
|
-
await storage.removeItem(storageKeys.activeSessionId);
|
|
521
|
-
updateSessionsRef.current(validSessions.filter(session => session.sessionId !== storedActiveSessionId), {
|
|
522
|
-
merge: false
|
|
665
|
+
const fullUser = await oxyServices.getCurrentUser();
|
|
666
|
+
await commitWebSession({
|
|
667
|
+
...callbackSession,
|
|
668
|
+
user: fullUser
|
|
523
669
|
});
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
670
|
+
return {
|
|
671
|
+
kind: 'session',
|
|
672
|
+
session: true
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
}, {
|
|
676
|
+
// 2) FedCM silent reauthn (Chrome). `silentSignInWithFedCM` plants
|
|
677
|
+
// the access token internally; we commit the returned session via
|
|
678
|
+
// `handleWebSSOSession`. Guarded so it fires at most once per page
|
|
679
|
+
// load across remounts.
|
|
680
|
+
id: 'fedcm-silent',
|
|
681
|
+
enabled: () => fedcmSupported && !servicesSilentAttempted.has(silentKey),
|
|
682
|
+
run: async () => {
|
|
683
|
+
servicesSilentAttempted.add(silentKey);
|
|
684
|
+
const session = await oxyServices.silentSignInWithFedCM?.();
|
|
685
|
+
if (!session || !commitWebSession) {
|
|
686
|
+
return {
|
|
687
|
+
kind: 'skip'
|
|
688
|
+
};
|
|
532
689
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
690
|
+
await commitWebSession(session);
|
|
691
|
+
return {
|
|
692
|
+
kind: 'session',
|
|
693
|
+
session: true
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}, {
|
|
697
|
+
// 3) Silent first-party iframe ({authWebUrl}/auth/silent) for
|
|
698
|
+
// browsers without FedCM (Safari / Firefox). After auto-detection
|
|
699
|
+
// `authWebUrl` is `auth.<rp-apex>`, so the iframe + its
|
|
700
|
+
// `fedcm_session` cookie are first-party with the RP. Shares the
|
|
701
|
+
// one-shot guard with the FedCM step.
|
|
702
|
+
id: 'silent-iframe',
|
|
703
|
+
enabled: () => (0, _useWebSSO.isWebBrowser)() && oxyServices.isFedCMSupported?.() !== true && !servicesSilentAttempted.has(silentKey),
|
|
704
|
+
run: async () => {
|
|
705
|
+
servicesSilentAttempted.add(silentKey);
|
|
706
|
+
const session = await oxyServices.silentSignIn?.();
|
|
707
|
+
if (!session || !commitWebSession) {
|
|
708
|
+
return {
|
|
709
|
+
kind: 'skip'
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
await commitWebSession(session);
|
|
713
|
+
return {
|
|
714
|
+
kind: 'session',
|
|
715
|
+
session: true
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
}, {
|
|
719
|
+
// 4) Refresh-cookie restore (same-site only). On `*.oxy.so` the
|
|
720
|
+
// httpOnly `oxy_rt_${n}` cookies ride along and resurrect every
|
|
721
|
+
// device-local slot. On a cross-domain RP (mention.earth, …) the
|
|
722
|
+
// cookie is `Domain=oxy.so` so it never reaches `api.<apex>` —
|
|
723
|
+
// `refreshAllSessions` returns `{accounts:[]}` and this skips.
|
|
724
|
+
// That is correct; there is deliberately NO `api.<apex>` bridge.
|
|
725
|
+
id: 'cookie-restore',
|
|
726
|
+
enabled: () => (0, _useWebSSO.isWebBrowser)(),
|
|
727
|
+
run: async () => {
|
|
728
|
+
const restored = await restoreViaRefreshCookie();
|
|
729
|
+
return restored ? {
|
|
730
|
+
kind: 'session',
|
|
731
|
+
session: true
|
|
732
|
+
} : {
|
|
733
|
+
kind: 'skip'
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
}, {
|
|
737
|
+
// 5) Stored-session bearer restore. NO `enabled` gate — runs on ALL
|
|
738
|
+
// platforms. This is native's ONLY restore path (every web-only
|
|
739
|
+
// step above is disabled off-browser).
|
|
740
|
+
id: 'stored-session',
|
|
741
|
+
run: async () => {
|
|
742
|
+
const restored = await restoreStoredSession();
|
|
743
|
+
return restored ? {
|
|
744
|
+
kind: 'session',
|
|
745
|
+
session: true
|
|
746
|
+
} : {
|
|
747
|
+
kind: 'skip'
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
}],
|
|
751
|
+
onStepError: (id, error) => {
|
|
752
|
+
if (__DEV__) {
|
|
753
|
+
_core.logger.debug(`Cold-boot step "${id}" errored (non-fatal, falling through)`, {
|
|
754
|
+
component: 'OxyContext',
|
|
755
|
+
method: 'restoreSessionsFromStorage'
|
|
756
|
+
}, error);
|
|
536
757
|
}
|
|
537
758
|
}
|
|
759
|
+
});
|
|
760
|
+
if (__DEV__ && outcome.kind === 'session') {
|
|
761
|
+
_core.logger.debug(`Cold boot recovered a session via "${outcome.via}"`, {
|
|
762
|
+
component: 'OxyContext',
|
|
763
|
+
method: 'restoreSessionsFromStorage'
|
|
764
|
+
});
|
|
538
765
|
}
|
|
539
766
|
} catch (error) {
|
|
540
767
|
if (__DEV__) {
|
|
@@ -547,7 +774,7 @@ const OxyProvider = ({
|
|
|
547
774
|
} finally {
|
|
548
775
|
setTokenReady(true);
|
|
549
776
|
}
|
|
550
|
-
}, [
|
|
777
|
+
}, [oxyServices, storage, restoreViaRefreshCookie, restoreStoredSession]);
|
|
551
778
|
(0, _react.useEffect)(() => {
|
|
552
779
|
if (!storage || initialized) {
|
|
553
780
|
return;
|
|
@@ -622,7 +849,21 @@ const OxyProvider = ({
|
|
|
622
849
|
onAuthStateChange?.(fullUser);
|
|
623
850
|
}, [oxyServices, updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, persistSessionDurably]);
|
|
624
851
|
|
|
625
|
-
//
|
|
852
|
+
// Expose `handleWebSSOSession` to the cold-boot FedCM/iframe/redirect steps,
|
|
853
|
+
// which reference it through a ref because they are declared above this
|
|
854
|
+
// callback. Assigned synchronously on every render so the ref is populated
|
|
855
|
+
// before the cold-boot effect (gated on `storage`/`initialized`) can fire.
|
|
856
|
+
handleWebSSOSessionRef.current = handleWebSSOSession;
|
|
857
|
+
|
|
858
|
+
// Cross-domain silent SSO is now owned by the `fedcm-silent` / `silent-iframe`
|
|
859
|
+
// cold-boot steps above (the ordered `runColdBoot` sequence). `useWebSSO`
|
|
860
|
+
// remains mounted for its module-level run-once guard and its interactive
|
|
861
|
+
// FedCM helpers, and as a bounded post-boot safety net: it can fire at most
|
|
862
|
+
// once per page load (its own module guard), and only AFTER cold boot has
|
|
863
|
+
// finished (`tokenReady`) with no user recovered. We deliberately keep
|
|
864
|
+
// `shouldTryWebSSO` as `tokenReady && !user && initialized` — it is NOT
|
|
865
|
+
// loosened; cold boot runs while `tokenReady` is false, so this never races
|
|
866
|
+
// the cold-boot silent step.
|
|
626
867
|
const shouldTryWebSSO = (0, _useWebSSO.isWebBrowser)() && tokenReady && !user && initialized;
|
|
627
868
|
(0, _useWebSSO.useWebSSO)({
|
|
628
869
|
oxyServices,
|
|
@@ -642,8 +883,16 @@ const OxyProvider = ({
|
|
|
642
883
|
// If session is gone (cleared/logged out), clear local session too
|
|
643
884
|
const lastIdPCheckRef = (0, _react.useRef)(0);
|
|
644
885
|
const pendingIdPCleanupRef = (0, _react.useRef)(null);
|
|
886
|
+
|
|
887
|
+
// Use the RESOLVED IdP origin (the auto-detected `auth.<rp-apex>` planted on
|
|
888
|
+
// the instance config), not the raw `authWebUrl` prop — on a cross-domain RP
|
|
889
|
+
// the prop is undefined but the instance was constructed with the detected
|
|
890
|
+
// value, so the check must target the same first-party IdP the cold-boot
|
|
891
|
+
// iframe used.
|
|
892
|
+
const resolvedAuthWebUrl = oxyServices.config?.authWebUrl;
|
|
645
893
|
(0, _react.useEffect)(() => {
|
|
646
894
|
if (!(0, _useWebSSO.isWebBrowser)() || !user || !initialized) return;
|
|
895
|
+
const idpOrigin = resolvedAuthWebUrl || 'https://auth.oxy.so';
|
|
647
896
|
const checkIdPSession = () => {
|
|
648
897
|
// Debounce: check at most once per 30 seconds
|
|
649
898
|
const now = Date.now();
|
|
@@ -656,7 +905,6 @@ const OxyProvider = ({
|
|
|
656
905
|
// Load hidden iframe to check IdP session via postMessage
|
|
657
906
|
const iframe = document.createElement('iframe');
|
|
658
907
|
iframe.style.cssText = 'display:none;width:0;height:0;border:0';
|
|
659
|
-
const idpOrigin = authWebUrl || 'https://auth.oxy.so';
|
|
660
908
|
iframe.src = `${idpOrigin}/auth/session-check?client_id=${encodeURIComponent(window.location.origin)}`;
|
|
661
909
|
let cleaned = false;
|
|
662
910
|
const cleanup = () => {
|
|
@@ -670,8 +918,15 @@ const OxyProvider = ({
|
|
|
670
918
|
if (event.data?.type !== 'oxy-session-check') return;
|
|
671
919
|
cleanup();
|
|
672
920
|
if (!event.data.hasSession) {
|
|
673
|
-
|
|
674
|
-
|
|
921
|
+
// Only a SAME-SITE, first-party IdP answer is authoritative enough to
|
|
922
|
+
// force a local sign-out. On a cross-site / undetermined IdP the
|
|
923
|
+
// "no session" answer must never clear local state (a third-party
|
|
924
|
+
// can't be trusted to end this app's session). Surface the toast in
|
|
925
|
+
// both cases, but gate the destructive `clearSessionState()`.
|
|
926
|
+
if (isSameSiteIdP(idpOrigin)) {
|
|
927
|
+
_bloom.toast.info('Your session has ended. Please sign in again.');
|
|
928
|
+
await clearSessionState();
|
|
929
|
+
}
|
|
675
930
|
}
|
|
676
931
|
};
|
|
677
932
|
window.addEventListener('message', handleMessage);
|
|
@@ -690,7 +945,7 @@ const OxyProvider = ({
|
|
|
690
945
|
pendingIdPCleanupRef.current?.();
|
|
691
946
|
pendingIdPCleanupRef.current = null;
|
|
692
947
|
};
|
|
693
|
-
}, [user, initialized, clearSessionState,
|
|
948
|
+
}, [user, initialized, clearSessionState, resolvedAuthWebUrl]);
|
|
694
949
|
const activeSession = activeSessionId ? sessions.find(session => session.sessionId === activeSessionId) : undefined;
|
|
695
950
|
const currentDeviceId = activeSession?.deviceId ?? null;
|
|
696
951
|
const userId = user?.id;
|