@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
|
@@ -177,6 +177,15 @@ function clearRefreshToken() {
|
|
|
177
177
|
} catch (err) {
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
function addTokenListener(listener) {
|
|
181
|
+
if (typeof listener !== "function") {
|
|
182
|
+
throw new Error("Token listener must be a function");
|
|
183
|
+
}
|
|
184
|
+
listeners.add(listener);
|
|
185
|
+
return () => {
|
|
186
|
+
listeners.delete(listener);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
180
189
|
if (typeof window !== "undefined" && window.addEventListener) {
|
|
181
190
|
window.addEventListener("storage", (event) => {
|
|
182
191
|
if (event.key !== "authToken") return;
|
|
@@ -225,6 +234,13 @@ var config = {
|
|
|
225
234
|
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
226
235
|
persistRefreshToken: false
|
|
227
236
|
};
|
|
237
|
+
var RUNTIME_POLICY_DEFAULTS = {
|
|
238
|
+
tokenRefreshBuffer: 60,
|
|
239
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
240
|
+
enableSessionValidation: true,
|
|
241
|
+
enableProactiveRefresh: true,
|
|
242
|
+
validateOnVisibility: true
|
|
243
|
+
};
|
|
228
244
|
function getConfig() {
|
|
229
245
|
return { ...config };
|
|
230
246
|
}
|
|
@@ -283,6 +299,28 @@ function acquireLoginLock(clientKey, redirectUri, ttlMs = 5e3) {
|
|
|
283
299
|
}
|
|
284
300
|
return true;
|
|
285
301
|
}
|
|
302
|
+
async function reportClientDiagnostic(safe) {
|
|
303
|
+
if (typeof fetch !== "function" || typeof getToken !== "function") return;
|
|
304
|
+
if (safe.outcome === "SUCCESS" && !safe.event.startsWith("PROFILE_") && !safe.event.startsWith("SESSION_")) return;
|
|
305
|
+
const { authBaseUrl } = getConfig();
|
|
306
|
+
const token = getToken();
|
|
307
|
+
if (!authBaseUrl || !token) return;
|
|
308
|
+
try {
|
|
309
|
+
await fetch(`${authBaseUrl.replace(/\/+$/, "")}/client-telemetry`, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
credentials: "include",
|
|
312
|
+
keepalive: true,
|
|
313
|
+
headers: {
|
|
314
|
+
"Content-Type": "application/json",
|
|
315
|
+
Authorization: `Bearer ${token}`,
|
|
316
|
+
"X-Correlation-ID": safe.correlationId,
|
|
317
|
+
"X-Request-ID": safe.correlationId
|
|
318
|
+
},
|
|
319
|
+
body: JSON.stringify(safe)
|
|
320
|
+
});
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
286
324
|
async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
287
325
|
const context = getDiagnosticContext();
|
|
288
326
|
const safe = {
|
|
@@ -299,12 +337,13 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
299
337
|
};
|
|
300
338
|
const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
|
|
301
339
|
console[method]("[auth-client]", safe);
|
|
340
|
+
void reportClientDiagnostic(safe);
|
|
302
341
|
return safe;
|
|
303
342
|
}
|
|
304
343
|
|
|
305
344
|
// core.js
|
|
306
345
|
var callbackProcessed = false;
|
|
307
|
-
function login(clientKeyArg, redirectUriArg) {
|
|
346
|
+
function login(clientKeyArg, redirectUriArg, options = {}) {
|
|
308
347
|
resetCallbackState();
|
|
309
348
|
const {
|
|
310
349
|
clientKey: defaultClientKey,
|
|
@@ -327,23 +366,24 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
327
366
|
sessionStorage.setItem("originalApp", clientKey);
|
|
328
367
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
329
368
|
if (isRouterMode()) {
|
|
330
|
-
return routerLogin(clientKey, redirectUri);
|
|
369
|
+
return routerLogin(clientKey, redirectUri, options);
|
|
331
370
|
} else {
|
|
332
|
-
return clientLogin(clientKey, redirectUri);
|
|
371
|
+
return clientLogin(clientKey, redirectUri, options);
|
|
333
372
|
}
|
|
334
373
|
}
|
|
335
|
-
function routerLogin(clientKey, redirectUri) {
|
|
374
|
+
function routerLogin(clientKey, redirectUri, options = {}) {
|
|
336
375
|
const { authBaseUrl } = getConfig();
|
|
337
376
|
const params = new URLSearchParams();
|
|
338
377
|
if (redirectUri) {
|
|
339
378
|
params.append("redirect_uri", redirectUri);
|
|
340
379
|
}
|
|
380
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
341
381
|
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
342
382
|
const query = params.toString();
|
|
343
383
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
344
384
|
window.location.href = backendLoginUrl;
|
|
345
385
|
}
|
|
346
|
-
function clientLogin(clientKey, redirectUri) {
|
|
386
|
+
function clientLogin(clientKey, redirectUri, options = {}) {
|
|
347
387
|
const { accountUiUrl } = getConfig();
|
|
348
388
|
const params = new URLSearchParams({
|
|
349
389
|
client: clientKey
|
|
@@ -351,6 +391,7 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
351
391
|
if (redirectUri) {
|
|
352
392
|
params.append("redirect_uri", redirectUri);
|
|
353
393
|
}
|
|
394
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
354
395
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
355
396
|
window.location.href = centralizedLoginUrl;
|
|
356
397
|
}
|
|
@@ -415,6 +456,28 @@ function resetCallbackState() {
|
|
|
415
456
|
}
|
|
416
457
|
var refreshInProgress = false;
|
|
417
458
|
var refreshPromise = null;
|
|
459
|
+
async function withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest) {
|
|
460
|
+
var _a;
|
|
461
|
+
const lockName = `auth-refresh-${clientKey}`;
|
|
462
|
+
const run = async () => {
|
|
463
|
+
try {
|
|
464
|
+
const persistedToken = localStorage.getItem("authToken");
|
|
465
|
+
if (tokenBeforeRefresh && persistedToken && persistedToken !== tokenBeforeRefresh) {
|
|
466
|
+
setToken(persistedToken);
|
|
467
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REUSED_CROSS_TAB", "SUCCESS", "CROSS_TAB_ROTATION", {
|
|
468
|
+
clientKey
|
|
469
|
+
});
|
|
470
|
+
return persistedToken;
|
|
471
|
+
}
|
|
472
|
+
} catch {
|
|
473
|
+
}
|
|
474
|
+
return refreshRequest();
|
|
475
|
+
};
|
|
476
|
+
if (typeof navigator !== "undefined" && ((_a = navigator.locks) == null ? void 0 : _a.request)) {
|
|
477
|
+
return navigator.locks.request(lockName, run);
|
|
478
|
+
}
|
|
479
|
+
return run();
|
|
480
|
+
}
|
|
418
481
|
async function refreshToken() {
|
|
419
482
|
const { clientKey, authBaseUrl } = getConfig();
|
|
420
483
|
if (refreshInProgress && refreshPromise) {
|
|
@@ -423,70 +486,74 @@ async function refreshToken() {
|
|
|
423
486
|
}
|
|
424
487
|
refreshInProgress = true;
|
|
425
488
|
refreshPromise = (async () => {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
hasStoredRefreshToken: !!storedRefreshToken
|
|
433
|
-
});
|
|
434
|
-
const requestOptions = {
|
|
435
|
-
method: "POST",
|
|
436
|
-
credentials: "include",
|
|
437
|
-
// ✅ Include httpOnly cookies (for HTTPS)
|
|
438
|
-
headers: {
|
|
439
|
-
...diagnosticHeaders(),
|
|
440
|
-
"Content-Type": "application/json"
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
if (storedRefreshToken) {
|
|
444
|
-
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
445
|
-
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
446
|
-
}
|
|
447
|
-
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
448
|
-
if (!response.ok) {
|
|
449
|
-
const errorText = await response.text();
|
|
450
|
-
let serverCode = null;
|
|
451
|
-
try {
|
|
452
|
-
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
453
|
-
} catch {
|
|
454
|
-
}
|
|
455
|
-
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
489
|
+
const tokenBeforeRefresh = getToken();
|
|
490
|
+
const refreshRequest = async () => {
|
|
491
|
+
var _a, _b, _c, _d, _e, _f;
|
|
492
|
+
try {
|
|
493
|
+
const storedRefreshToken = getRefreshToken();
|
|
494
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
456
495
|
clientKey,
|
|
457
|
-
|
|
496
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
497
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
458
498
|
});
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
499
|
+
const requestOptions = {
|
|
500
|
+
method: "POST",
|
|
501
|
+
credentials: "include",
|
|
502
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
503
|
+
headers: {
|
|
504
|
+
...diagnosticHeaders(),
|
|
505
|
+
"Content-Type": "application/json"
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
if (storedRefreshToken) {
|
|
509
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
510
|
+
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
511
|
+
}
|
|
512
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
513
|
+
if (!response.ok) {
|
|
514
|
+
const errorText = await response.text();
|
|
515
|
+
let serverCode = null;
|
|
516
|
+
try {
|
|
517
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
518
|
+
} catch {
|
|
519
|
+
}
|
|
520
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
521
|
+
clientKey,
|
|
522
|
+
status: response.status
|
|
523
|
+
});
|
|
524
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
525
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
526
|
+
refreshError.status = response.status;
|
|
527
|
+
throw refreshError;
|
|
528
|
+
}
|
|
529
|
+
const data = await response.json();
|
|
530
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
531
|
+
if (!access_token) {
|
|
532
|
+
throw new Error("No access token in refresh response");
|
|
533
|
+
}
|
|
534
|
+
setToken(access_token);
|
|
535
|
+
if (new_refresh_token) {
|
|
536
|
+
setRefreshToken(new_refresh_token);
|
|
537
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
538
|
+
}
|
|
539
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
540
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
541
|
+
return access_token;
|
|
542
|
+
} catch (err) {
|
|
543
|
+
console.error("\u274C Token refresh error:", err);
|
|
544
|
+
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"));
|
|
545
|
+
if (isAuthRejection) {
|
|
546
|
+
clearToken();
|
|
547
|
+
clearRefreshToken();
|
|
548
|
+
}
|
|
549
|
+
throw err;
|
|
483
550
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
})
|
|
551
|
+
};
|
|
552
|
+
return withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest);
|
|
553
|
+
})().finally(() => {
|
|
554
|
+
refreshInProgress = false;
|
|
555
|
+
refreshPromise = null;
|
|
556
|
+
});
|
|
490
557
|
return refreshPromise;
|
|
491
558
|
}
|
|
492
559
|
async function validateCurrentSession() {
|
|
@@ -738,23 +805,72 @@ function stopSessionSecurity() {
|
|
|
738
805
|
|
|
739
806
|
// react/AuthProvider.jsx
|
|
740
807
|
var AuthContext = (0, import_react.createContext)();
|
|
741
|
-
function AuthProvider({ children, onSessionExpired }) {
|
|
808
|
+
function AuthProvider({ children, onSessionExpired, manageSessionSecurity = true }) {
|
|
742
809
|
const [token, setTokenState] = (0, import_react.useState)(getToken());
|
|
743
810
|
const [user, setUser] = (0, import_react.useState)(null);
|
|
744
811
|
const [loading, setLoading] = (0, import_react.useState)(!!token);
|
|
745
812
|
const [sessionValid, setSessionValid] = (0, import_react.useState)(true);
|
|
746
813
|
const sessionSecurityRef = (0, import_react.useRef)(null);
|
|
814
|
+
const onSessionExpiredRef = (0, import_react.useRef)(onSessionExpired);
|
|
815
|
+
const recoveryInFlightRef = (0, import_react.useRef)(null);
|
|
816
|
+
const profileRecoveryTokensRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
|
|
817
|
+
(0, import_react.useEffect)(() => {
|
|
818
|
+
onSessionExpiredRef.current = onSessionExpired;
|
|
819
|
+
}, [onSessionExpired]);
|
|
820
|
+
(0, import_react.useEffect)(() => {
|
|
821
|
+
const unsubscribe = addTokenListener((nextToken, previousToken) => {
|
|
822
|
+
if (nextToken === previousToken) return;
|
|
823
|
+
setTokenState(nextToken);
|
|
824
|
+
if (nextToken) {
|
|
825
|
+
setSessionValid(true);
|
|
826
|
+
setLoading(true);
|
|
827
|
+
} else {
|
|
828
|
+
setSessionValid(false);
|
|
829
|
+
setUser(null);
|
|
830
|
+
setLoading(false);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
return unsubscribe;
|
|
834
|
+
}, []);
|
|
835
|
+
const invalidateLocalSession = () => {
|
|
836
|
+
clearToken();
|
|
837
|
+
setTokenState(null);
|
|
838
|
+
setUser(null);
|
|
839
|
+
setSessionValid(false);
|
|
840
|
+
setLoading(false);
|
|
841
|
+
};
|
|
747
842
|
const handleSessionInvalid = (reason) => {
|
|
748
843
|
console.log("\u{1F6A8} AuthProvider: Session invalidated -", reason);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
844
|
+
if (recoveryInFlightRef.current) return recoveryInFlightRef.current;
|
|
845
|
+
const recover = async () => {
|
|
846
|
+
const callback = onSessionExpiredRef.current;
|
|
847
|
+
if (typeof callback === "function") {
|
|
848
|
+
try {
|
|
849
|
+
await callback(reason);
|
|
850
|
+
const recoveredToken = getToken();
|
|
851
|
+
if (recoveredToken) {
|
|
852
|
+
setTokenState(recoveredToken);
|
|
853
|
+
setSessionValid(true);
|
|
854
|
+
setLoading(true);
|
|
855
|
+
return true;
|
|
856
|
+
}
|
|
857
|
+
} catch (error) {
|
|
858
|
+
await emitAuthDiagnostic("SESSION_RECOVERY_FAILED", "FAILURE", reason, {
|
|
859
|
+
status: (error == null ? void 0 : error.status) || null,
|
|
860
|
+
errorCode: (error == null ? void 0 : error.code) || null
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
invalidateLocalSession();
|
|
865
|
+
return false;
|
|
866
|
+
};
|
|
867
|
+
recoveryInFlightRef.current = recover().finally(() => {
|
|
868
|
+
recoveryInFlightRef.current = null;
|
|
869
|
+
});
|
|
870
|
+
return recoveryInFlightRef.current;
|
|
755
871
|
};
|
|
756
872
|
(0, import_react.useEffect)(() => {
|
|
757
|
-
if (token && !sessionSecurityRef.current) {
|
|
873
|
+
if (manageSessionSecurity && token && !sessionSecurityRef.current) {
|
|
758
874
|
console.log("\u{1F510} AuthProvider: Starting session security");
|
|
759
875
|
const unsubscribe = onSessionInvalid(handleSessionInvalid);
|
|
760
876
|
sessionSecurityRef.current = startSessionSecurity(handleSessionInvalid);
|
|
@@ -766,11 +882,11 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
766
882
|
}
|
|
767
883
|
};
|
|
768
884
|
}
|
|
769
|
-
if (!token && sessionSecurityRef.current) {
|
|
885
|
+
if ((!manageSessionSecurity || !token) && sessionSecurityRef.current) {
|
|
770
886
|
sessionSecurityRef.current.stopAll();
|
|
771
887
|
sessionSecurityRef.current = null;
|
|
772
888
|
}
|
|
773
|
-
}, [token]);
|
|
889
|
+
}, [manageSessionSecurity, token]);
|
|
774
890
|
(0, import_react.useEffect)(() => {
|
|
775
891
|
console.log("\u{1F50D} AuthProvider useEffect triggered:", {
|
|
776
892
|
hasToken: !!token,
|
|
@@ -791,26 +907,79 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
791
907
|
authBaseUrl,
|
|
792
908
|
tokenPreview: token.slice(0, 50) + "..."
|
|
793
909
|
});
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
910
|
+
let cancelled = false;
|
|
911
|
+
const fetchProfile = async (accessToken2) => {
|
|
912
|
+
const response = await fetch(`${authBaseUrl}/account/profile`, {
|
|
913
|
+
headers: { Authorization: `Bearer ${accessToken2}` },
|
|
914
|
+
credentials: "include"
|
|
915
|
+
});
|
|
916
|
+
console.log("\u{1F4E5} Profile response status:", response.status);
|
|
917
|
+
if (!response.ok) {
|
|
918
|
+
const error = new Error(`Profile request failed: ${response.status}`);
|
|
919
|
+
error.status = response.status;
|
|
920
|
+
throw error;
|
|
921
|
+
}
|
|
922
|
+
return response.json();
|
|
923
|
+
};
|
|
924
|
+
const loadProfile = async () => {
|
|
925
|
+
try {
|
|
926
|
+
const responseBody = await fetchProfile(token);
|
|
927
|
+
if (cancelled) return;
|
|
928
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
929
|
+
console.log("\u2705 Profile fetched successfully:", userData.email);
|
|
930
|
+
setUser(userData);
|
|
931
|
+
setSessionValid(true);
|
|
932
|
+
setLoading(false);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
if (cancelled) return;
|
|
935
|
+
const status = Number((error == null ? void 0 : error.status) || 0);
|
|
936
|
+
const isUnauthorized = status === 401;
|
|
937
|
+
const callback = onSessionExpiredRef.current;
|
|
938
|
+
await emitAuthDiagnostic(
|
|
939
|
+
"PROFILE_REQUEST_FAILED",
|
|
940
|
+
isUnauthorized ? "FAILURE" : "WARNING",
|
|
941
|
+
isUnauthorized ? "PROFILE_UNAUTHORIZED" : status ? `PROFILE_HTTP_${status}` : "PROFILE_NETWORK_ERROR",
|
|
942
|
+
{ status, errorName: error == null ? void 0 : error.name, errorCode: error == null ? void 0 : error.code }
|
|
943
|
+
);
|
|
944
|
+
if (!isUnauthorized) {
|
|
945
|
+
console.warn("\u26A0\uFE0F Profile unavailable; retaining the current auth session", error);
|
|
946
|
+
setLoading(false);
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
if (typeof callback === "function" && !profileRecoveryTokensRef.current.has(token)) {
|
|
950
|
+
profileRecoveryTokensRef.current.add(token);
|
|
951
|
+
try {
|
|
952
|
+
await callback("profile_unauthorized");
|
|
953
|
+
const recoveredToken = getToken();
|
|
954
|
+
if (recoveredToken && recoveredToken !== token) {
|
|
955
|
+
setTokenState(recoveredToken);
|
|
956
|
+
setSessionValid(true);
|
|
957
|
+
setLoading(true);
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
if (recoveredToken) {
|
|
961
|
+
const responseBody = await fetchProfile(recoveredToken);
|
|
962
|
+
if (cancelled) return;
|
|
963
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
964
|
+
setUser(userData);
|
|
965
|
+
setSessionValid(true);
|
|
966
|
+
setLoading(false);
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
} catch (recoveryError) {
|
|
970
|
+
await emitAuthDiagnostic("PROFILE_RECOVERY_FAILED", "FAILURE", "PROFILE_REFRESH_FAILED", {
|
|
971
|
+
status: (recoveryError == null ? void 0 : recoveryError.status) || null,
|
|
972
|
+
errorCode: (recoveryError == null ? void 0 : recoveryError.code) || null
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
invalidateLocalSession();
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
void loadProfile();
|
|
980
|
+
return () => {
|
|
981
|
+
cancelled = true;
|
|
982
|
+
};
|
|
814
983
|
}, [token]);
|
|
815
984
|
const login2 = (clientKey, redirectUri, state) => {
|
|
816
985
|
login(clientKey, redirectUri, state);
|
|
@@ -839,6 +1008,7 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
839
1008
|
setToken(newToken);
|
|
840
1009
|
setTokenState(newToken);
|
|
841
1010
|
setSessionValid(true);
|
|
1011
|
+
if (newToken) setLoading(true);
|
|
842
1012
|
},
|
|
843
1013
|
clearToken: () => {
|
|
844
1014
|
stopSessionSecurity();
|