antaeus.keycloak.react 2.0.0 → 2.1.1
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/index.js +82 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var defaultConfig = {
|
|
|
17
17
|
scope: "openid profile email offline_access",
|
|
18
18
|
features: {
|
|
19
19
|
deviceFlow: false,
|
|
20
|
-
sessionMonitor:
|
|
20
|
+
sessionMonitor: true,
|
|
21
21
|
autoRefresh: true
|
|
22
22
|
},
|
|
23
23
|
silentRenew: {
|
|
@@ -62,7 +62,11 @@ var KeycloakConfigBuilder = class {
|
|
|
62
62
|
scope: config.scope,
|
|
63
63
|
automaticSilentRenew: config.silentRenew.enabled,
|
|
64
64
|
loadUserInfo: config.advanced.loadUserInfo,
|
|
65
|
-
silent_redirect_uri: config.silentRenew.silentRedirectUri
|
|
65
|
+
silent_redirect_uri: config.silentRenew.silentRedirectUri,
|
|
66
|
+
// Add PKCE support (required for public clients)
|
|
67
|
+
...config.advanced.codeChallengeMethod && {
|
|
68
|
+
response_mode: "query"
|
|
69
|
+
}
|
|
66
70
|
};
|
|
67
71
|
return oidcSettings;
|
|
68
72
|
}
|
|
@@ -200,14 +204,14 @@ function useTokenLifecycle(options) {
|
|
|
200
204
|
log("Refresh already in progress, skipping");
|
|
201
205
|
return;
|
|
202
206
|
}
|
|
203
|
-
|
|
204
|
-
log("Refresh token exceeded max lifetime, cannot refresh");
|
|
205
|
-
setIsRefreshTokenExpired(true);
|
|
206
|
-
onRefreshTokenExpired?.();
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
207
|
+
setIsRefreshing(true);
|
|
209
208
|
try {
|
|
210
|
-
|
|
209
|
+
if (checkRefreshTokenLifetime()) {
|
|
210
|
+
log("Refresh token exceeded max lifetime, cannot refresh");
|
|
211
|
+
setIsRefreshTokenExpired(true);
|
|
212
|
+
onRefreshTokenExpired?.();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
211
215
|
onRefreshStart?.();
|
|
212
216
|
log("Starting token refresh");
|
|
213
217
|
await auth.signinSilent();
|
|
@@ -242,7 +246,11 @@ function useTokenLifecycle(options) {
|
|
|
242
246
|
onRefreshTokenExpired?.();
|
|
243
247
|
const key = getStorageKey();
|
|
244
248
|
localStorage.removeItem(key);
|
|
245
|
-
|
|
249
|
+
try {
|
|
250
|
+
await auth.removeUser();
|
|
251
|
+
} catch (error) {
|
|
252
|
+
log("Error removing user after max lifetime:", error);
|
|
253
|
+
}
|
|
246
254
|
return false;
|
|
247
255
|
}
|
|
248
256
|
const expiresAt = auth.user.expires_at;
|
|
@@ -321,7 +329,9 @@ function useTokenLifecycle(options) {
|
|
|
321
329
|
onRefreshTokenExpired?.();
|
|
322
330
|
const key = getStorageKey();
|
|
323
331
|
localStorage.removeItem(key);
|
|
324
|
-
auth.removeUser()
|
|
332
|
+
auth.removeUser().catch((error) => {
|
|
333
|
+
log("Error removing user during periodic check:", error);
|
|
334
|
+
});
|
|
325
335
|
}
|
|
326
336
|
}, 5 * 60 * 1e3);
|
|
327
337
|
return () => clearInterval(intervalId);
|
|
@@ -498,22 +508,28 @@ var SessionMonitor = ({
|
|
|
498
508
|
}) => {
|
|
499
509
|
const auth = useAuth();
|
|
500
510
|
const [timeRemaining, setTimeRemaining] = react.useState(null);
|
|
511
|
+
const [initialTimeRemaining, setInitialTimeRemaining] = react.useState(null);
|
|
501
512
|
const [minimized, setMinimized] = react.useState(startMinimized);
|
|
502
513
|
const [warningTriggered, setWarningTriggered] = react.useState(false);
|
|
503
514
|
const [criticalTriggered, setCriticalTriggered] = react.useState(false);
|
|
504
515
|
react.useEffect(() => {
|
|
505
516
|
if (!auth.isAuthenticated || !auth.user?.expires_at) {
|
|
506
517
|
setTimeRemaining(null);
|
|
518
|
+
setInitialTimeRemaining(null);
|
|
507
519
|
return;
|
|
508
520
|
}
|
|
521
|
+
let logoutTimeoutId = null;
|
|
509
522
|
const updateTimeRemaining = () => {
|
|
510
523
|
const expiresAt = auth.user.expires_at;
|
|
511
524
|
const now = Math.floor(Date.now() / 1e3);
|
|
512
525
|
const remaining = expiresAt - now;
|
|
526
|
+
if (initialTimeRemaining === null && remaining > 0) {
|
|
527
|
+
setInitialTimeRemaining(remaining);
|
|
528
|
+
}
|
|
513
529
|
if (remaining <= 0) {
|
|
514
530
|
setTimeRemaining(0);
|
|
515
531
|
onExpired?.();
|
|
516
|
-
setTimeout(() => {
|
|
532
|
+
logoutTimeoutId = setTimeout(() => {
|
|
517
533
|
auth.signoutRedirect();
|
|
518
534
|
}, 2e3);
|
|
519
535
|
} else {
|
|
@@ -529,13 +545,19 @@ var SessionMonitor = ({
|
|
|
529
545
|
};
|
|
530
546
|
updateTimeRemaining();
|
|
531
547
|
const interval = setInterval(updateTimeRemaining, 1e3);
|
|
532
|
-
return () =>
|
|
548
|
+
return () => {
|
|
549
|
+
clearInterval(interval);
|
|
550
|
+
if (logoutTimeoutId) {
|
|
551
|
+
clearTimeout(logoutTimeoutId);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
533
554
|
}, [
|
|
534
555
|
auth,
|
|
535
556
|
warningThreshold,
|
|
536
557
|
criticalThreshold,
|
|
537
558
|
warningTriggered,
|
|
538
559
|
criticalTriggered,
|
|
560
|
+
initialTimeRemaining,
|
|
539
561
|
onWarning,
|
|
540
562
|
onCritical,
|
|
541
563
|
onExpired
|
|
@@ -558,9 +580,9 @@ var SessionMonitor = ({
|
|
|
558
580
|
return "normal";
|
|
559
581
|
}, [timeRemaining, warningThreshold, criticalThreshold]);
|
|
560
582
|
const getProgress = react.useCallback(() => {
|
|
561
|
-
if (!timeRemaining || !
|
|
562
|
-
return timeRemaining /
|
|
563
|
-
}, [timeRemaining,
|
|
583
|
+
if (!timeRemaining || !initialTimeRemaining) return 100;
|
|
584
|
+
return Math.max(0, Math.min(100, timeRemaining / initialTimeRemaining * 100));
|
|
585
|
+
}, [timeRemaining, initialTimeRemaining]);
|
|
564
586
|
if (!auth.isAuthenticated || timeRemaining === null) {
|
|
565
587
|
return null;
|
|
566
588
|
}
|
|
@@ -873,12 +895,27 @@ function buildZeroConfig(props) {
|
|
|
873
895
|
const { authority, clientId, preset = "default", config: overrides = {} } = props;
|
|
874
896
|
const presetConfig = mergeWithPreset(preset, overrides);
|
|
875
897
|
const fullConfig = {
|
|
876
|
-
...defaultConfig,
|
|
877
|
-
...presetConfig,
|
|
878
898
|
authority,
|
|
879
899
|
clientId,
|
|
880
|
-
redirectUri: presetConfig.redirectUri || (typeof window !== "undefined" ? window.location.origin + "/" : "/"),
|
|
881
|
-
postLogoutRedirectUri: presetConfig.postLogoutRedirectUri || (typeof window !== "undefined" ? window.location.origin + "/" : "/")
|
|
900
|
+
redirectUri: presetConfig.redirectUri || defaultConfig.redirectUri || (typeof window !== "undefined" ? window.location.origin + "/" : "/"),
|
|
901
|
+
postLogoutRedirectUri: presetConfig.postLogoutRedirectUri || defaultConfig.postLogoutRedirectUri || (typeof window !== "undefined" ? window.location.origin + "/" : "/"),
|
|
902
|
+
scope: presetConfig.scope || defaultConfig.scope,
|
|
903
|
+
features: {
|
|
904
|
+
...defaultConfig.features,
|
|
905
|
+
...presetConfig.features
|
|
906
|
+
},
|
|
907
|
+
silentRenew: {
|
|
908
|
+
...defaultConfig.silentRenew,
|
|
909
|
+
...presetConfig.silentRenew
|
|
910
|
+
},
|
|
911
|
+
advanced: {
|
|
912
|
+
...defaultConfig.advanced,
|
|
913
|
+
...presetConfig.advanced
|
|
914
|
+
},
|
|
915
|
+
events: {
|
|
916
|
+
...defaultConfig.events,
|
|
917
|
+
...presetConfig.events
|
|
918
|
+
}
|
|
882
919
|
};
|
|
883
920
|
return fullConfig;
|
|
884
921
|
}
|
|
@@ -973,6 +1010,7 @@ var ProtectedRoute = ({
|
|
|
973
1010
|
const { hasAnyRole, hasAllRoles } = useRoles();
|
|
974
1011
|
const { hasAnyScope, hasAllScopes } = useScopes();
|
|
975
1012
|
const location = reactRouterDom.useLocation();
|
|
1013
|
+
const hasRedirected = react.useRef(false);
|
|
976
1014
|
const isAuthenticated = !auth.isLoading && auth.isAuthenticated;
|
|
977
1015
|
const policyAccess = policy && auth.user ? policy(auth.user, { location }) : null;
|
|
978
1016
|
const roleAccess = requiredRoles.length > 0 ? requireAllRoles ? hasAllRoles(requiredRoles) : hasAnyRole(requiredRoles) : null;
|
|
@@ -984,12 +1022,21 @@ var ProtectedRoute = ({
|
|
|
984
1022
|
}
|
|
985
1023
|
}, [unauthorizedReason, onUnauthorized]);
|
|
986
1024
|
react.useEffect(() => {
|
|
987
|
-
if (!auth.isLoading && !auth.isAuthenticated) {
|
|
1025
|
+
if (!auth.isLoading && !auth.isAuthenticated && !hasRedirected.current) {
|
|
1026
|
+
hasRedirected.current = true;
|
|
988
1027
|
auth.signinRedirect({
|
|
989
1028
|
redirect_uri: window.location.origin + location.pathname + location.search
|
|
1029
|
+
}).catch((error) => {
|
|
1030
|
+
hasRedirected.current = false;
|
|
1031
|
+
console.error("[ProtectedRoute] Signin redirect failed:", error);
|
|
990
1032
|
});
|
|
991
1033
|
}
|
|
992
|
-
}, [auth.isLoading, auth.isAuthenticated,
|
|
1034
|
+
}, [auth.isLoading, auth.isAuthenticated, location.pathname, location.search]);
|
|
1035
|
+
react.useEffect(() => {
|
|
1036
|
+
if (auth.isAuthenticated) {
|
|
1037
|
+
hasRedirected.current = false;
|
|
1038
|
+
}
|
|
1039
|
+
}, [auth.isAuthenticated]);
|
|
993
1040
|
if (auth.isLoading) {
|
|
994
1041
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent || /* @__PURE__ */ jsxRuntime.jsx(DefaultLoadingComponent, {}) });
|
|
995
1042
|
}
|
|
@@ -1357,8 +1404,17 @@ var DeviceLogin = ({
|
|
|
1357
1404
|
});
|
|
1358
1405
|
if (userManagerRef.current) {
|
|
1359
1406
|
await userManagerRef.current.storeUser(user);
|
|
1407
|
+
const events = userManagerRef.current.events;
|
|
1408
|
+
if (events && events._userLoaded) {
|
|
1409
|
+
events._userLoaded.raise(user);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
if (onSuccess) {
|
|
1413
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1414
|
+
onSuccess(user);
|
|
1415
|
+
} else {
|
|
1416
|
+
window.location.reload();
|
|
1360
1417
|
}
|
|
1361
|
-
onSuccess?.(user);
|
|
1362
1418
|
}
|
|
1363
1419
|
} catch (err) {
|
|
1364
1420
|
if (!err.message.includes("authorization_pending")) {
|
|
@@ -1445,14 +1501,14 @@ function useProtectedFetch(options = {}) {
|
|
|
1445
1501
|
},
|
|
1446
1502
|
[baseUrl]
|
|
1447
1503
|
);
|
|
1448
|
-
const getAuthHeader =
|
|
1504
|
+
const getAuthHeader = () => {
|
|
1449
1505
|
if (!auth.user?.access_token) {
|
|
1450
1506
|
return {};
|
|
1451
1507
|
}
|
|
1452
1508
|
return {
|
|
1453
1509
|
Authorization: `Bearer ${auth.user.access_token}`
|
|
1454
1510
|
};
|
|
1455
|
-
}
|
|
1511
|
+
};
|
|
1456
1512
|
const buildHeaders = react.useCallback(
|
|
1457
1513
|
(requestHeaders) => {
|
|
1458
1514
|
const headers = new Headers(requestHeaders);
|
|
@@ -1465,7 +1521,7 @@ function useProtectedFetch(options = {}) {
|
|
|
1465
1521
|
});
|
|
1466
1522
|
return headers;
|
|
1467
1523
|
},
|
|
1468
|
-
[
|
|
1524
|
+
[customHeaders]
|
|
1469
1525
|
);
|
|
1470
1526
|
const protectedFetch = react.useCallback(
|
|
1471
1527
|
async (url, init) => {
|