@oxyhq/services 6.10.5 → 6.10.6
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 +3 -3
- package/lib/commonjs/ui/hooks/useWebSSO.js +45 -7
- package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
- package/lib/module/ui/hooks/useWebSSO.js +45 -7
- package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/hooks/useWebSSO.ts +46 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive TypeScript UI library for the Oxy API providing authentication, user management, and UI components for React Native and Expo applications.
|
|
4
4
|
|
|
5
|
-
**Current published version: 6.10.
|
|
5
|
+
**Current published version: 6.10.5**
|
|
6
6
|
|
|
7
7
|
> **For web apps (Vite, Next.js, CRA):** Use [`@oxyhq/auth`](../auth) for authentication and [`@oxyhq/core`](../core) for types and services.
|
|
8
8
|
>
|
|
@@ -747,11 +747,11 @@ Typed returns are defined in `ui/hooks/queries/paymentTypes.ts` (`Subscription`,
|
|
|
747
747
|
|
|
748
748
|
## Sign-In Token Planting
|
|
749
749
|
|
|
750
|
-
`
|
|
750
|
+
`@oxyhq/core` `OxyServices.verifyChallenge()` plants `setTokens(accessToken, refreshToken ?? '')` internally before returning. `useAuthOperations.performSignIn` no longer needs to hand-plant the token or fall back to `getTokenBySession` — just await `verifyChallenge` and proceed.
|
|
751
751
|
|
|
752
752
|
## Silent SSO Run-Once Guard
|
|
753
753
|
|
|
754
|
-
`
|
|
754
|
+
The cross-page-load deduplication guard for `navigator.credentials.get({mediation:'silent'})` lives in `@oxyhq/core` `OxyServices.silentSignInWithFedCM()` (module-level memo keyed on `origin+baseURL`). `useWebSSO` in this package keeps only a per-instance `hasCheckedRef` fast-path — no cross-mount guard.
|
|
755
755
|
|
|
756
756
|
## Requirements
|
|
757
757
|
|
|
@@ -23,6 +23,36 @@ var _react = require("react");
|
|
|
23
23
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/FedCM_API
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Module-level guard tracking which (origin + API) signatures have already
|
|
28
|
+
* had a silent SSO attempt this page load.
|
|
29
|
+
*
|
|
30
|
+
* A per-component `useRef` guard resets whenever the provider remounts (route
|
|
31
|
+
* churn, StrictMode double-invoke, error-boundary recovery), which previously
|
|
32
|
+
* allowed silent SSO to re-fire and — combined with a routing redirect loop —
|
|
33
|
+
* produced an accelerating `navigator.credentials.get` retry storm. Keying the
|
|
34
|
+
* guard on a stable signature instead of the component instance makes silent
|
|
35
|
+
* SSO fire EXACTLY ONCE per page load regardless of how many times the
|
|
36
|
+
* provider mounts. The set is intentionally never cleared: a fresh page load
|
|
37
|
+
* (the only thing that can change the answer) starts a fresh module scope.
|
|
38
|
+
*/
|
|
39
|
+
const silentSSOAttempted = new Set();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build a stable signature for the silent-SSO run-once guard. Two providers
|
|
43
|
+
* pointed at the same API from the same origin share one attempt.
|
|
44
|
+
*/
|
|
45
|
+
function ssoSignature(oxyServices) {
|
|
46
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : 'no-origin';
|
|
47
|
+
let baseURL = '';
|
|
48
|
+
try {
|
|
49
|
+
baseURL = oxyServices.getBaseURL?.() ?? '';
|
|
50
|
+
} catch {
|
|
51
|
+
baseURL = '';
|
|
52
|
+
}
|
|
53
|
+
return `${origin}|${baseURL}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
26
56
|
/**
|
|
27
57
|
* Check if we're running in a web browser environment (not React Native)
|
|
28
58
|
*/
|
|
@@ -140,12 +170,12 @@ function useWebSSO({
|
|
|
140
170
|
|
|
141
171
|
// Auto-check SSO on mount (web only, FedCM only, not on auth domain).
|
|
142
172
|
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
173
|
+
// Run-once is enforced by TWO guards:
|
|
174
|
+
// 1. `hasCheckedRef` — cheap per-instance fast-path so effect re-runs
|
|
175
|
+
// (from changing deps) within one mount never re-fire.
|
|
176
|
+
// 2. `silentSSOAttempted` — module-level, survives remounts/StrictMode so
|
|
177
|
+
// silent SSO fires exactly once per page load even if the provider
|
|
178
|
+
// unmounts and remounts.
|
|
149
179
|
(0, _react.useEffect)(() => {
|
|
150
180
|
if (!enabled || !isWebBrowser() || hasCheckedRef.current || isIdentityProvider(authWebUrl)) {
|
|
151
181
|
if (isIdentityProvider(authWebUrl)) {
|
|
@@ -153,13 +183,21 @@ function useWebSSO({
|
|
|
153
183
|
}
|
|
154
184
|
return;
|
|
155
185
|
}
|
|
186
|
+
const signature = ssoSignature(oxyServices);
|
|
187
|
+
if (silentSSOAttempted.has(signature)) {
|
|
188
|
+
// Already attempted this page load (e.g. before a remount) — do not
|
|
189
|
+
// re-fire. Mark the local fast-path too so subsequent re-renders skip.
|
|
190
|
+
hasCheckedRef.current = true;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
156
193
|
hasCheckedRef.current = true;
|
|
194
|
+
silentSSOAttempted.add(signature);
|
|
157
195
|
if (fedCMSupported) {
|
|
158
196
|
checkSSO();
|
|
159
197
|
} else {
|
|
160
198
|
onSSOUnavailable?.();
|
|
161
199
|
}
|
|
162
|
-
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, authWebUrl]);
|
|
200
|
+
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, oxyServices, authWebUrl]);
|
|
163
201
|
return {
|
|
164
202
|
checkSSO,
|
|
165
203
|
signInWithFedCM,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","require","
|
|
1
|
+
{"version":3,"names":["_react","require","silentSSOAttempted","Set","ssoSignature","oxyServices","origin","window","location","baseURL","getBaseURL","isWebBrowser","document","documentElement","isIdentityProvider","authWebUrl","hostname","idpHostname","URL","useWebSSO","onSessionFound","onSSOUnavailable","onError","enabled","isCheckingRef","useRef","hasCheckedRef","fedCMSupported","isFedCMSupported","config","checkSSO","useCallback","current","session","silentSignInWithFedCM","error","Error","String","signInWithFedCM","useEffect","signature","has","add","isChecking"],"sourceRoot":"../../../../src","sources":["ui/hooks/useWebSSO.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -18,6 +18,36 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { useEffect, useRef, useCallback } from 'react';
|
|
21
|
+
/**
|
|
22
|
+
* Module-level guard tracking which (origin + API) signatures have already
|
|
23
|
+
* had a silent SSO attempt this page load.
|
|
24
|
+
*
|
|
25
|
+
* A per-component `useRef` guard resets whenever the provider remounts (route
|
|
26
|
+
* churn, StrictMode double-invoke, error-boundary recovery), which previously
|
|
27
|
+
* allowed silent SSO to re-fire and — combined with a routing redirect loop —
|
|
28
|
+
* produced an accelerating `navigator.credentials.get` retry storm. Keying the
|
|
29
|
+
* guard on a stable signature instead of the component instance makes silent
|
|
30
|
+
* SSO fire EXACTLY ONCE per page load regardless of how many times the
|
|
31
|
+
* provider mounts. The set is intentionally never cleared: a fresh page load
|
|
32
|
+
* (the only thing that can change the answer) starts a fresh module scope.
|
|
33
|
+
*/
|
|
34
|
+
const silentSSOAttempted = new Set();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build a stable signature for the silent-SSO run-once guard. Two providers
|
|
38
|
+
* pointed at the same API from the same origin share one attempt.
|
|
39
|
+
*/
|
|
40
|
+
function ssoSignature(oxyServices) {
|
|
41
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : 'no-origin';
|
|
42
|
+
let baseURL = '';
|
|
43
|
+
try {
|
|
44
|
+
baseURL = oxyServices.getBaseURL?.() ?? '';
|
|
45
|
+
} catch {
|
|
46
|
+
baseURL = '';
|
|
47
|
+
}
|
|
48
|
+
return `${origin}|${baseURL}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
21
51
|
/**
|
|
22
52
|
* Check if we're running in a web browser environment (not React Native)
|
|
23
53
|
*/
|
|
@@ -135,12 +165,12 @@ export function useWebSSO({
|
|
|
135
165
|
|
|
136
166
|
// Auto-check SSO on mount (web only, FedCM only, not on auth domain).
|
|
137
167
|
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
168
|
+
// Run-once is enforced by TWO guards:
|
|
169
|
+
// 1. `hasCheckedRef` — cheap per-instance fast-path so effect re-runs
|
|
170
|
+
// (from changing deps) within one mount never re-fire.
|
|
171
|
+
// 2. `silentSSOAttempted` — module-level, survives remounts/StrictMode so
|
|
172
|
+
// silent SSO fires exactly once per page load even if the provider
|
|
173
|
+
// unmounts and remounts.
|
|
144
174
|
useEffect(() => {
|
|
145
175
|
if (!enabled || !isWebBrowser() || hasCheckedRef.current || isIdentityProvider(authWebUrl)) {
|
|
146
176
|
if (isIdentityProvider(authWebUrl)) {
|
|
@@ -148,13 +178,21 @@ export function useWebSSO({
|
|
|
148
178
|
}
|
|
149
179
|
return;
|
|
150
180
|
}
|
|
181
|
+
const signature = ssoSignature(oxyServices);
|
|
182
|
+
if (silentSSOAttempted.has(signature)) {
|
|
183
|
+
// Already attempted this page load (e.g. before a remount) — do not
|
|
184
|
+
// re-fire. Mark the local fast-path too so subsequent re-renders skip.
|
|
185
|
+
hasCheckedRef.current = true;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
151
188
|
hasCheckedRef.current = true;
|
|
189
|
+
silentSSOAttempted.add(signature);
|
|
152
190
|
if (fedCMSupported) {
|
|
153
191
|
checkSSO();
|
|
154
192
|
} else {
|
|
155
193
|
onSSOUnavailable?.();
|
|
156
194
|
}
|
|
157
|
-
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, authWebUrl]);
|
|
195
|
+
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, oxyServices, authWebUrl]);
|
|
158
196
|
return {
|
|
159
197
|
checkSSO,
|
|
160
198
|
signInWithFedCM,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useEffect","useRef","useCallback","
|
|
1
|
+
{"version":3,"names":["useEffect","useRef","useCallback","silentSSOAttempted","Set","ssoSignature","oxyServices","origin","window","location","baseURL","getBaseURL","isWebBrowser","document","documentElement","isIdentityProvider","authWebUrl","hostname","idpHostname","URL","useWebSSO","onSessionFound","onSSOUnavailable","onError","enabled","isCheckingRef","hasCheckedRef","fedCMSupported","isFedCMSupported","config","checkSSO","current","session","silentSignInWithFedCM","error","Error","String","signInWithFedCM","signature","has","add","isChecking"],"sourceRoot":"../../../../src","sources":["ui/hooks/useWebSSO.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebSSO.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useWebSSO.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,UAAU,gBAAgB;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACrD,2DAA2D;IAC3D,eAAe,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC5D,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"useWebSSO.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useWebSSO.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,UAAU,gBAAgB;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACrD,2DAA2D;IAC3D,eAAe,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC5D,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAgCD;;GAEG;AACH,iBAAS,YAAY,IAAI,OAAO,CAI/B;AAgBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,OAAO,EACP,OAAc,GACf,EAAE,gBAAgB,GAAG,eAAe,CAwHpC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useWebSSO.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useWebSSO.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,UAAU,gBAAgB;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACrD,2DAA2D;IAC3D,eAAe,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC5D,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"useWebSSO.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useWebSSO.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,UAAU,gBAAgB;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACrD,2DAA2D;IAC3D,eAAe,EAAE,MAAM,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IAC5D,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAgCD;;GAEG;AACH,iBAAS,YAAY,IAAI,OAAO,CAI/B;AAgBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,OAAO,EACP,OAAc,GACf,EAAE,gBAAgB,GAAG,eAAe,CAwHpC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -38,6 +38,36 @@ interface UseWebSSOResult {
|
|
|
38
38
|
isFedCMSupported: boolean;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Module-level guard tracking which (origin + API) signatures have already
|
|
43
|
+
* had a silent SSO attempt this page load.
|
|
44
|
+
*
|
|
45
|
+
* A per-component `useRef` guard resets whenever the provider remounts (route
|
|
46
|
+
* churn, StrictMode double-invoke, error-boundary recovery), which previously
|
|
47
|
+
* allowed silent SSO to re-fire and — combined with a routing redirect loop —
|
|
48
|
+
* produced an accelerating `navigator.credentials.get` retry storm. Keying the
|
|
49
|
+
* guard on a stable signature instead of the component instance makes silent
|
|
50
|
+
* SSO fire EXACTLY ONCE per page load regardless of how many times the
|
|
51
|
+
* provider mounts. The set is intentionally never cleared: a fresh page load
|
|
52
|
+
* (the only thing that can change the answer) starts a fresh module scope.
|
|
53
|
+
*/
|
|
54
|
+
const silentSSOAttempted = new Set<string>();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build a stable signature for the silent-SSO run-once guard. Two providers
|
|
58
|
+
* pointed at the same API from the same origin share one attempt.
|
|
59
|
+
*/
|
|
60
|
+
function ssoSignature(oxyServices: OxyServices): string {
|
|
61
|
+
const origin = typeof window !== 'undefined' ? window.location.origin : 'no-origin';
|
|
62
|
+
let baseURL = '';
|
|
63
|
+
try {
|
|
64
|
+
baseURL = oxyServices.getBaseURL?.() ?? '';
|
|
65
|
+
} catch {
|
|
66
|
+
baseURL = '';
|
|
67
|
+
}
|
|
68
|
+
return `${origin}|${baseURL}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
41
71
|
/**
|
|
42
72
|
* Check if we're running in a web browser environment (not React Native)
|
|
43
73
|
*/
|
|
@@ -165,12 +195,12 @@ export function useWebSSO({
|
|
|
165
195
|
|
|
166
196
|
// Auto-check SSO on mount (web only, FedCM only, not on auth domain).
|
|
167
197
|
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
198
|
+
// Run-once is enforced by TWO guards:
|
|
199
|
+
// 1. `hasCheckedRef` — cheap per-instance fast-path so effect re-runs
|
|
200
|
+
// (from changing deps) within one mount never re-fire.
|
|
201
|
+
// 2. `silentSSOAttempted` — module-level, survives remounts/StrictMode so
|
|
202
|
+
// silent SSO fires exactly once per page load even if the provider
|
|
203
|
+
// unmounts and remounts.
|
|
174
204
|
useEffect(() => {
|
|
175
205
|
if (!enabled || !isWebBrowser() || hasCheckedRef.current || isIdentityProvider(authWebUrl)) {
|
|
176
206
|
if (isIdentityProvider(authWebUrl)) {
|
|
@@ -179,14 +209,23 @@ export function useWebSSO({
|
|
|
179
209
|
return;
|
|
180
210
|
}
|
|
181
211
|
|
|
212
|
+
const signature = ssoSignature(oxyServices);
|
|
213
|
+
if (silentSSOAttempted.has(signature)) {
|
|
214
|
+
// Already attempted this page load (e.g. before a remount) — do not
|
|
215
|
+
// re-fire. Mark the local fast-path too so subsequent re-renders skip.
|
|
216
|
+
hasCheckedRef.current = true;
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
182
220
|
hasCheckedRef.current = true;
|
|
221
|
+
silentSSOAttempted.add(signature);
|
|
183
222
|
|
|
184
223
|
if (fedCMSupported) {
|
|
185
224
|
checkSSO();
|
|
186
225
|
} else {
|
|
187
226
|
onSSOUnavailable?.();
|
|
188
227
|
}
|
|
189
|
-
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, authWebUrl]);
|
|
228
|
+
}, [enabled, checkSSO, fedCMSupported, onSSOUnavailable, oxyServices, authWebUrl]);
|
|
190
229
|
|
|
191
230
|
return {
|
|
192
231
|
checkSSO,
|