@oxyhq/auth 2.0.6 → 2.0.8
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/README.md +15 -1
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/WebOxyProvider.js +49 -8
- package/dist/cjs/hooks/useWebSSO.js +8 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/WebOxyProvider.js +49 -8
- package/dist/esm/hooks/useWebSSO.js +8 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/WebOxyProvider.tsx +49 -7
- package/src/hooks/useWebSSO.ts +8 -1
|
@@ -11,6 +11,38 @@ import { OxyServices, CrossDomainAuth, createAuthManager, } from '@oxyhq/core';
|
|
|
11
11
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
12
12
|
import { attachQueryPersistence, createQueryClient } from './hooks/queryClient';
|
|
13
13
|
const WebOxyContext = createContext(null);
|
|
14
|
+
/**
|
|
15
|
+
* Module-level run-once guard for the provider's silent sign-in step.
|
|
16
|
+
*
|
|
17
|
+
* The init effect runs again whenever the provider remounts (route change,
|
|
18
|
+
* StrictMode double-invoke, error-boundary recovery). The redirect-callback
|
|
19
|
+
* and local-session-restore steps are cheap and idempotent, but
|
|
20
|
+
* `crossDomainAuth.silentSignIn()` is NOT: it first tries FedCM silent
|
|
21
|
+
* mediation (`navigator.credentials.get`) and, if that yields nothing, falls
|
|
22
|
+
* back to an iframe-based silent auth against `/auth/silent`.
|
|
23
|
+
*
|
|
24
|
+
* The FedCM leg is now centrally memoized inside `@oxyhq/core`'s
|
|
25
|
+
* `silentSignInWithFedCM` (at-most-once `navigator.credentials.get` per page
|
|
26
|
+
* load). The iframe fallback, however, is a SEPARATE mechanism the core guard
|
|
27
|
+
* does not cover — without this guard a remount storm would create a hidden
|
|
28
|
+
* iframe per remount. So this guard is intentionally retained to keep the
|
|
29
|
+
* provider's WHOLE silent step run-once. Keyed on `origin + baseURL` to match
|
|
30
|
+
* the core guard's keying (so the two stay in lockstep) and to survive
|
|
31
|
+
* instance churn; never cleared because only a fresh page load can change the
|
|
32
|
+
* IdP/iframe session state.
|
|
33
|
+
*/
|
|
34
|
+
const silentSignInAttempted = new Set();
|
|
35
|
+
function silentSignInKey(oxyServices) {
|
|
36
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : 'no-origin';
|
|
37
|
+
let baseURL = '';
|
|
38
|
+
try {
|
|
39
|
+
baseURL = oxyServices.getBaseURL();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
baseURL = '';
|
|
43
|
+
}
|
|
44
|
+
return `${origin}|${baseURL}`;
|
|
45
|
+
}
|
|
14
46
|
/**
|
|
15
47
|
* Web-only Oxy Provider
|
|
16
48
|
*
|
|
@@ -112,15 +144,24 @@ export function WebOxyProvider({ children, baseURL, authWebUrl, onAuthStateChang
|
|
|
112
144
|
await authManager.signOut();
|
|
113
145
|
}
|
|
114
146
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
// Silent sign-in: run AT MOST ONCE per page load. A remount (route
|
|
148
|
+
// change / StrictMode / error recovery) must not re-trigger the
|
|
149
|
+
// browser credential request OR the iframe fallback. The FedCM leg is
|
|
150
|
+
// additionally memoized in core; this guard also covers the iframe
|
|
151
|
+
// fallback that core does not.
|
|
152
|
+
const ssoKey = silentSignInKey(oxyServices);
|
|
153
|
+
if (!silentSignInAttempted.has(ssoKey)) {
|
|
154
|
+
silentSignInAttempted.add(ssoKey);
|
|
155
|
+
try {
|
|
156
|
+
const session = await crossDomainAuth.silentSignIn();
|
|
157
|
+
if (mounted && session?.user) {
|
|
158
|
+
await handleAuthSuccess(session, 'fedcm');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Silent sign-in failed — resolve to unauthenticated below.
|
|
120
164
|
}
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
// Silent sign-in failed
|
|
124
165
|
}
|
|
125
166
|
if (mounted)
|
|
126
167
|
setIsLoading(false);
|
|
@@ -117,7 +117,14 @@ export function useWebSSO({ oxyServices, onSessionFound, onSSOUnavailable, onErr
|
|
|
117
117
|
isCheckingRef.current = false;
|
|
118
118
|
}
|
|
119
119
|
}, [oxyServices, onSessionFound, onError, fedCMSupported]);
|
|
120
|
-
// Auto-check SSO on mount (web only, FedCM only, not on auth domain)
|
|
120
|
+
// Auto-check SSO on mount (web only, FedCM only, not on auth domain).
|
|
121
|
+
//
|
|
122
|
+
// `hasCheckedRef` is a cheap per-instance fast-path so effect re-runs (from
|
|
123
|
+
// changing deps) within one mount never re-fire. The page-load run-once
|
|
124
|
+
// guarantee — silent SSO invoking `navigator.credentials.get` AT MOST ONCE
|
|
125
|
+
// per page load across remounts / StrictMode / multiple consumers — lives in
|
|
126
|
+
// `@oxyhq/core`'s `silentSignInWithFedCM`, which memoizes the first silent
|
|
127
|
+
// attempt's result for this origin + API. The hook therefore calls it freely.
|
|
121
128
|
useEffect(() => {
|
|
122
129
|
if (!enabled || !isWebBrowser() || hasCheckedRef.current || isIdentityProvider()) {
|
|
123
130
|
if (isIdentityProvider()) {
|