@spidy092/auth-client 3.1.2 → 3.1.4
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/dist/api.cjs +117 -61
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +117 -61
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +290 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +290 -96
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +117 -61
- package/dist/preferences.cjs.map +1 -1
- package/dist/preferences.js +117 -61
- package/dist/preferences.js.map +1 -1
- package/dist/react/AuthProvider.cjs +266 -96
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +266 -96
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +117 -61
- package/dist/react/PreferencesProvider.cjs.map +1 -1
- package/dist/react/PreferencesProvider.js +117 -61
- package/dist/react/PreferencesProvider.js.map +1 -1
- package/dist/react/useAuth.cjs +7 -0
- package/dist/react/useAuth.cjs.map +1 -1
- package/dist/react/useAuth.js +7 -0
- package/dist/react/useAuth.js.map +1 -1
- package/dist/react/useSessionMonitor.cjs +157 -66
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +157 -66
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/package.json +1 -1
|
@@ -143,6 +143,15 @@ function clearRefreshToken() {
|
|
|
143
143
|
} catch (err) {
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
+
function addTokenListener(listener) {
|
|
147
|
+
if (typeof listener !== "function") {
|
|
148
|
+
throw new Error("Token listener must be a function");
|
|
149
|
+
}
|
|
150
|
+
listeners.add(listener);
|
|
151
|
+
return () => {
|
|
152
|
+
listeners.delete(listener);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
146
155
|
if (typeof window !== "undefined" && window.addEventListener) {
|
|
147
156
|
window.addEventListener("storage", (event) => {
|
|
148
157
|
if (event.key !== "authToken") return;
|
|
@@ -191,6 +200,13 @@ var config = {
|
|
|
191
200
|
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
192
201
|
persistRefreshToken: false
|
|
193
202
|
};
|
|
203
|
+
var RUNTIME_POLICY_DEFAULTS = {
|
|
204
|
+
tokenRefreshBuffer: 60,
|
|
205
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
206
|
+
enableSessionValidation: true,
|
|
207
|
+
enableProactiveRefresh: true,
|
|
208
|
+
validateOnVisibility: true
|
|
209
|
+
};
|
|
194
210
|
function getConfig() {
|
|
195
211
|
return { ...config };
|
|
196
212
|
}
|
|
@@ -249,6 +265,28 @@ function acquireLoginLock(clientKey, redirectUri, ttlMs = 5e3) {
|
|
|
249
265
|
}
|
|
250
266
|
return true;
|
|
251
267
|
}
|
|
268
|
+
async function reportClientDiagnostic(safe) {
|
|
269
|
+
if (typeof fetch !== "function" || typeof getToken !== "function") return;
|
|
270
|
+
if (safe.outcome === "SUCCESS" && !safe.event.startsWith("PROFILE_") && !safe.event.startsWith("SESSION_")) return;
|
|
271
|
+
const { authBaseUrl } = getConfig();
|
|
272
|
+
const token = getToken();
|
|
273
|
+
if (!authBaseUrl || !token) return;
|
|
274
|
+
try {
|
|
275
|
+
await fetch(`${authBaseUrl.replace(/\/+$/, "")}/client-telemetry`, {
|
|
276
|
+
method: "POST",
|
|
277
|
+
credentials: "include",
|
|
278
|
+
keepalive: true,
|
|
279
|
+
headers: {
|
|
280
|
+
"Content-Type": "application/json",
|
|
281
|
+
Authorization: `Bearer ${token}`,
|
|
282
|
+
"X-Correlation-ID": safe.correlationId,
|
|
283
|
+
"X-Request-ID": safe.correlationId
|
|
284
|
+
},
|
|
285
|
+
body: JSON.stringify(safe)
|
|
286
|
+
});
|
|
287
|
+
} catch {
|
|
288
|
+
}
|
|
289
|
+
}
|
|
252
290
|
async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
253
291
|
const context = getDiagnosticContext();
|
|
254
292
|
const safe = {
|
|
@@ -265,12 +303,13 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
265
303
|
};
|
|
266
304
|
const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
|
|
267
305
|
console[method]("[auth-client]", safe);
|
|
306
|
+
void reportClientDiagnostic(safe);
|
|
268
307
|
return safe;
|
|
269
308
|
}
|
|
270
309
|
|
|
271
310
|
// core.js
|
|
272
311
|
var callbackProcessed = false;
|
|
273
|
-
function login(clientKeyArg, redirectUriArg) {
|
|
312
|
+
function login(clientKeyArg, redirectUriArg, options = {}) {
|
|
274
313
|
resetCallbackState();
|
|
275
314
|
const {
|
|
276
315
|
clientKey: defaultClientKey,
|
|
@@ -293,23 +332,24 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
293
332
|
sessionStorage.setItem("originalApp", clientKey);
|
|
294
333
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
295
334
|
if (isRouterMode()) {
|
|
296
|
-
return routerLogin(clientKey, redirectUri);
|
|
335
|
+
return routerLogin(clientKey, redirectUri, options);
|
|
297
336
|
} else {
|
|
298
|
-
return clientLogin(clientKey, redirectUri);
|
|
337
|
+
return clientLogin(clientKey, redirectUri, options);
|
|
299
338
|
}
|
|
300
339
|
}
|
|
301
|
-
function routerLogin(clientKey, redirectUri) {
|
|
340
|
+
function routerLogin(clientKey, redirectUri, options = {}) {
|
|
302
341
|
const { authBaseUrl } = getConfig();
|
|
303
342
|
const params = new URLSearchParams();
|
|
304
343
|
if (redirectUri) {
|
|
305
344
|
params.append("redirect_uri", redirectUri);
|
|
306
345
|
}
|
|
346
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
307
347
|
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
308
348
|
const query = params.toString();
|
|
309
349
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
310
350
|
window.location.href = backendLoginUrl;
|
|
311
351
|
}
|
|
312
|
-
function clientLogin(clientKey, redirectUri) {
|
|
352
|
+
function clientLogin(clientKey, redirectUri, options = {}) {
|
|
313
353
|
const { accountUiUrl } = getConfig();
|
|
314
354
|
const params = new URLSearchParams({
|
|
315
355
|
client: clientKey
|
|
@@ -317,6 +357,7 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
317
357
|
if (redirectUri) {
|
|
318
358
|
params.append("redirect_uri", redirectUri);
|
|
319
359
|
}
|
|
360
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
320
361
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
321
362
|
window.location.href = centralizedLoginUrl;
|
|
322
363
|
}
|
|
@@ -381,6 +422,28 @@ function resetCallbackState() {
|
|
|
381
422
|
}
|
|
382
423
|
var refreshInProgress = false;
|
|
383
424
|
var refreshPromise = null;
|
|
425
|
+
async function withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest) {
|
|
426
|
+
var _a;
|
|
427
|
+
const lockName = `auth-refresh-${clientKey}`;
|
|
428
|
+
const run = async () => {
|
|
429
|
+
try {
|
|
430
|
+
const persistedToken = localStorage.getItem("authToken");
|
|
431
|
+
if (tokenBeforeRefresh && persistedToken && persistedToken !== tokenBeforeRefresh) {
|
|
432
|
+
setToken(persistedToken);
|
|
433
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REUSED_CROSS_TAB", "SUCCESS", "CROSS_TAB_ROTATION", {
|
|
434
|
+
clientKey
|
|
435
|
+
});
|
|
436
|
+
return persistedToken;
|
|
437
|
+
}
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
return refreshRequest();
|
|
441
|
+
};
|
|
442
|
+
if (typeof navigator !== "undefined" && ((_a = navigator.locks) == null ? void 0 : _a.request)) {
|
|
443
|
+
return navigator.locks.request(lockName, run);
|
|
444
|
+
}
|
|
445
|
+
return run();
|
|
446
|
+
}
|
|
384
447
|
async function refreshToken() {
|
|
385
448
|
const { clientKey, authBaseUrl } = getConfig();
|
|
386
449
|
if (refreshInProgress && refreshPromise) {
|
|
@@ -389,70 +452,74 @@ async function refreshToken() {
|
|
|
389
452
|
}
|
|
390
453
|
refreshInProgress = true;
|
|
391
454
|
refreshPromise = (async () => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
hasStoredRefreshToken: !!storedRefreshToken
|
|
399
|
-
});
|
|
400
|
-
const requestOptions = {
|
|
401
|
-
method: "POST",
|
|
402
|
-
credentials: "include",
|
|
403
|
-
// ✅ Include httpOnly cookies (for HTTPS)
|
|
404
|
-
headers: {
|
|
405
|
-
...diagnosticHeaders(),
|
|
406
|
-
"Content-Type": "application/json"
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
if (storedRefreshToken) {
|
|
410
|
-
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
411
|
-
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
412
|
-
}
|
|
413
|
-
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
414
|
-
if (!response.ok) {
|
|
415
|
-
const errorText = await response.text();
|
|
416
|
-
let serverCode = null;
|
|
417
|
-
try {
|
|
418
|
-
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
419
|
-
} catch {
|
|
420
|
-
}
|
|
421
|
-
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
455
|
+
const tokenBeforeRefresh = getToken();
|
|
456
|
+
const refreshRequest = async () => {
|
|
457
|
+
var _a, _b, _c, _d, _e, _f;
|
|
458
|
+
try {
|
|
459
|
+
const storedRefreshToken = getRefreshToken();
|
|
460
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
422
461
|
clientKey,
|
|
423
|
-
|
|
462
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
463
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
424
464
|
});
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
465
|
+
const requestOptions = {
|
|
466
|
+
method: "POST",
|
|
467
|
+
credentials: "include",
|
|
468
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
469
|
+
headers: {
|
|
470
|
+
...diagnosticHeaders(),
|
|
471
|
+
"Content-Type": "application/json"
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
if (storedRefreshToken) {
|
|
475
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
476
|
+
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
477
|
+
}
|
|
478
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
479
|
+
if (!response.ok) {
|
|
480
|
+
const errorText = await response.text();
|
|
481
|
+
let serverCode = null;
|
|
482
|
+
try {
|
|
483
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
487
|
+
clientKey,
|
|
488
|
+
status: response.status
|
|
489
|
+
});
|
|
490
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
491
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
492
|
+
refreshError.status = response.status;
|
|
493
|
+
throw refreshError;
|
|
494
|
+
}
|
|
495
|
+
const data = await response.json();
|
|
496
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
497
|
+
if (!access_token) {
|
|
498
|
+
throw new Error("No access token in refresh response");
|
|
499
|
+
}
|
|
500
|
+
setToken(access_token);
|
|
501
|
+
if (new_refresh_token) {
|
|
502
|
+
setRefreshToken(new_refresh_token);
|
|
503
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
504
|
+
}
|
|
505
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
506
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
507
|
+
return access_token;
|
|
508
|
+
} catch (err) {
|
|
509
|
+
console.error("\u274C Token refresh error:", err);
|
|
510
|
+
const isAuthRejection = ((_c = err.message) == null ? void 0 : _c.includes("401")) || ((_d = err.message) == null ? void 0 : _d.includes("403")) || ((_e = err.message) == null ? void 0 : _e.includes("invalid_grant")) || ((_f = err.message) == null ? void 0 : _f.includes("Refresh failed: 4"));
|
|
511
|
+
if (isAuthRejection) {
|
|
512
|
+
clearToken();
|
|
513
|
+
clearRefreshToken();
|
|
514
|
+
}
|
|
515
|
+
throw err;
|
|
449
516
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
})
|
|
517
|
+
};
|
|
518
|
+
return withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest);
|
|
519
|
+
})().finally(() => {
|
|
520
|
+
refreshInProgress = false;
|
|
521
|
+
refreshPromise = null;
|
|
522
|
+
});
|
|
456
523
|
return refreshPromise;
|
|
457
524
|
}
|
|
458
525
|
async function validateCurrentSession() {
|
|
@@ -704,23 +771,72 @@ function stopSessionSecurity() {
|
|
|
704
771
|
|
|
705
772
|
// react/AuthProvider.jsx
|
|
706
773
|
var AuthContext = createContext();
|
|
707
|
-
function AuthProvider({ children, onSessionExpired }) {
|
|
774
|
+
function AuthProvider({ children, onSessionExpired, manageSessionSecurity = true }) {
|
|
708
775
|
const [token, setTokenState] = useState(getToken());
|
|
709
776
|
const [user, setUser] = useState(null);
|
|
710
777
|
const [loading, setLoading] = useState(!!token);
|
|
711
778
|
const [sessionValid, setSessionValid] = useState(true);
|
|
712
779
|
const sessionSecurityRef = useRef(null);
|
|
780
|
+
const onSessionExpiredRef = useRef(onSessionExpired);
|
|
781
|
+
const recoveryInFlightRef = useRef(null);
|
|
782
|
+
const profileRecoveryTokensRef = useRef(/* @__PURE__ */ new Set());
|
|
783
|
+
useEffect(() => {
|
|
784
|
+
onSessionExpiredRef.current = onSessionExpired;
|
|
785
|
+
}, [onSessionExpired]);
|
|
786
|
+
useEffect(() => {
|
|
787
|
+
const unsubscribe = addTokenListener((nextToken, previousToken) => {
|
|
788
|
+
if (nextToken === previousToken) return;
|
|
789
|
+
setTokenState(nextToken);
|
|
790
|
+
if (nextToken) {
|
|
791
|
+
setSessionValid(true);
|
|
792
|
+
setLoading(true);
|
|
793
|
+
} else {
|
|
794
|
+
setSessionValid(false);
|
|
795
|
+
setUser(null);
|
|
796
|
+
setLoading(false);
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
return unsubscribe;
|
|
800
|
+
}, []);
|
|
801
|
+
const invalidateLocalSession = () => {
|
|
802
|
+
clearToken();
|
|
803
|
+
setTokenState(null);
|
|
804
|
+
setUser(null);
|
|
805
|
+
setSessionValid(false);
|
|
806
|
+
setLoading(false);
|
|
807
|
+
};
|
|
713
808
|
const handleSessionInvalid = (reason) => {
|
|
714
809
|
console.log("\u{1F6A8} AuthProvider: Session invalidated -", reason);
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
810
|
+
if (recoveryInFlightRef.current) return recoveryInFlightRef.current;
|
|
811
|
+
const recover = async () => {
|
|
812
|
+
const callback = onSessionExpiredRef.current;
|
|
813
|
+
if (typeof callback === "function") {
|
|
814
|
+
try {
|
|
815
|
+
await callback(reason);
|
|
816
|
+
const recoveredToken = getToken();
|
|
817
|
+
if (recoveredToken) {
|
|
818
|
+
setTokenState(recoveredToken);
|
|
819
|
+
setSessionValid(true);
|
|
820
|
+
setLoading(true);
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
} catch (error) {
|
|
824
|
+
await emitAuthDiagnostic("SESSION_RECOVERY_FAILED", "FAILURE", reason, {
|
|
825
|
+
status: (error == null ? void 0 : error.status) || null,
|
|
826
|
+
errorCode: (error == null ? void 0 : error.code) || null
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
invalidateLocalSession();
|
|
831
|
+
return false;
|
|
832
|
+
};
|
|
833
|
+
recoveryInFlightRef.current = recover().finally(() => {
|
|
834
|
+
recoveryInFlightRef.current = null;
|
|
835
|
+
});
|
|
836
|
+
return recoveryInFlightRef.current;
|
|
721
837
|
};
|
|
722
838
|
useEffect(() => {
|
|
723
|
-
if (token && !sessionSecurityRef.current) {
|
|
839
|
+
if (manageSessionSecurity && token && !sessionSecurityRef.current) {
|
|
724
840
|
console.log("\u{1F510} AuthProvider: Starting session security");
|
|
725
841
|
const unsubscribe = onSessionInvalid(handleSessionInvalid);
|
|
726
842
|
sessionSecurityRef.current = startSessionSecurity(handleSessionInvalid);
|
|
@@ -732,11 +848,11 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
732
848
|
}
|
|
733
849
|
};
|
|
734
850
|
}
|
|
735
|
-
if (!token && sessionSecurityRef.current) {
|
|
851
|
+
if ((!manageSessionSecurity || !token) && sessionSecurityRef.current) {
|
|
736
852
|
sessionSecurityRef.current.stopAll();
|
|
737
853
|
sessionSecurityRef.current = null;
|
|
738
854
|
}
|
|
739
|
-
}, [token]);
|
|
855
|
+
}, [manageSessionSecurity, token]);
|
|
740
856
|
useEffect(() => {
|
|
741
857
|
console.log("\u{1F50D} AuthProvider useEffect triggered:", {
|
|
742
858
|
hasToken: !!token,
|
|
@@ -757,26 +873,79 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
757
873
|
authBaseUrl,
|
|
758
874
|
tokenPreview: token.slice(0, 50) + "..."
|
|
759
875
|
});
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
876
|
+
let cancelled = false;
|
|
877
|
+
const fetchProfile = async (accessToken2) => {
|
|
878
|
+
const response = await fetch(`${authBaseUrl}/account/profile`, {
|
|
879
|
+
headers: { Authorization: `Bearer ${accessToken2}` },
|
|
880
|
+
credentials: "include"
|
|
881
|
+
});
|
|
882
|
+
console.log("\u{1F4E5} Profile response status:", response.status);
|
|
883
|
+
if (!response.ok) {
|
|
884
|
+
const error = new Error(`Profile request failed: ${response.status}`);
|
|
885
|
+
error.status = response.status;
|
|
886
|
+
throw error;
|
|
887
|
+
}
|
|
888
|
+
return response.json();
|
|
889
|
+
};
|
|
890
|
+
const loadProfile = async () => {
|
|
891
|
+
try {
|
|
892
|
+
const responseBody = await fetchProfile(token);
|
|
893
|
+
if (cancelled) return;
|
|
894
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
895
|
+
console.log("\u2705 Profile fetched successfully:", userData.email);
|
|
896
|
+
setUser(userData);
|
|
897
|
+
setSessionValid(true);
|
|
898
|
+
setLoading(false);
|
|
899
|
+
} catch (error) {
|
|
900
|
+
if (cancelled) return;
|
|
901
|
+
const status = Number((error == null ? void 0 : error.status) || 0);
|
|
902
|
+
const isUnauthorized = status === 401;
|
|
903
|
+
const callback = onSessionExpiredRef.current;
|
|
904
|
+
await emitAuthDiagnostic(
|
|
905
|
+
"PROFILE_REQUEST_FAILED",
|
|
906
|
+
isUnauthorized ? "FAILURE" : "WARNING",
|
|
907
|
+
isUnauthorized ? "PROFILE_UNAUTHORIZED" : status ? `PROFILE_HTTP_${status}` : "PROFILE_NETWORK_ERROR",
|
|
908
|
+
{ status, errorName: error == null ? void 0 : error.name, errorCode: error == null ? void 0 : error.code }
|
|
909
|
+
);
|
|
910
|
+
if (!isUnauthorized) {
|
|
911
|
+
console.warn("\u26A0\uFE0F Profile unavailable; retaining the current auth session", error);
|
|
912
|
+
setLoading(false);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (typeof callback === "function" && !profileRecoveryTokensRef.current.has(token)) {
|
|
916
|
+
profileRecoveryTokensRef.current.add(token);
|
|
917
|
+
try {
|
|
918
|
+
await callback("profile_unauthorized");
|
|
919
|
+
const recoveredToken = getToken();
|
|
920
|
+
if (recoveredToken && recoveredToken !== token) {
|
|
921
|
+
setTokenState(recoveredToken);
|
|
922
|
+
setSessionValid(true);
|
|
923
|
+
setLoading(true);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (recoveredToken) {
|
|
927
|
+
const responseBody = await fetchProfile(recoveredToken);
|
|
928
|
+
if (cancelled) return;
|
|
929
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
930
|
+
setUser(userData);
|
|
931
|
+
setSessionValid(true);
|
|
932
|
+
setLoading(false);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
} catch (recoveryError) {
|
|
936
|
+
await emitAuthDiagnostic("PROFILE_RECOVERY_FAILED", "FAILURE", "PROFILE_REFRESH_FAILED", {
|
|
937
|
+
status: (recoveryError == null ? void 0 : recoveryError.status) || null,
|
|
938
|
+
errorCode: (recoveryError == null ? void 0 : recoveryError.code) || null
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
invalidateLocalSession();
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
void loadProfile();
|
|
946
|
+
return () => {
|
|
947
|
+
cancelled = true;
|
|
948
|
+
};
|
|
780
949
|
}, [token]);
|
|
781
950
|
const login2 = (clientKey, redirectUri, state) => {
|
|
782
951
|
login(clientKey, redirectUri, state);
|
|
@@ -805,6 +974,7 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
805
974
|
setToken(newToken);
|
|
806
975
|
setTokenState(newToken);
|
|
807
976
|
setSessionValid(true);
|
|
977
|
+
if (newToken) setLoading(true);
|
|
808
978
|
},
|
|
809
979
|
clearToken: () => {
|
|
810
980
|
stopSessionSecurity();
|