antaeus.keycloak.react 2.6.2 → 2.7.0

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.mjs CHANGED
@@ -33,7 +33,8 @@ var defaultConfig = {
33
33
  // 30 days in seconds
34
34
  showLoadingIndicator: false,
35
35
  loadingMessage: "Refreshing session...",
36
- useRefreshToken: true
36
+ useRefreshToken: true,
37
+ refreshOnResume: true
37
38
  },
38
39
  advanced: {
39
40
  responseType: "code",
@@ -188,6 +189,7 @@ function useTokenLifecycle(options) {
188
189
  const auth = useAuth();
189
190
  const [isRefreshing, setIsRefreshing] = useState(false);
190
191
  const [isRefreshTokenExpired, setIsRefreshTokenExpired] = useState(false);
192
+ const isRefreshingRef = useRef(false);
191
193
  const hasCheckedOnStartup = useRef(false);
192
194
  const supportsRefreshTokens = config.silentRenew?.useRefreshToken !== false;
193
195
  const [pendingRetry, setPendingRetry] = useState(null);
@@ -240,10 +242,11 @@ function useTokenLifecycle(options) {
240
242
  return elapsedSeconds >= maxLifetime;
241
243
  }, [config.silentRenew?.maxRefreshTokenLifetime, getStorageKey, log, storeFirstLoginTime]);
242
244
  const refreshToken = useCallback(async () => {
243
- if (isRefreshing) {
245
+ if (isRefreshingRef.current) {
244
246
  log("Refresh already in progress, skipping");
245
247
  return;
246
248
  }
249
+ isRefreshingRef.current = true;
247
250
  setIsRefreshing(true);
248
251
  deferredRefreshReasonRef.current = null;
249
252
  try {
@@ -299,10 +302,10 @@ function useTokenLifecycle(options) {
299
302
  onRefreshError?.(err);
300
303
  throw err;
301
304
  } finally {
305
+ isRefreshingRef.current = false;
302
306
  setIsRefreshing(false);
303
307
  }
304
308
  }, [
305
- isRefreshing,
306
309
  auth,
307
310
  checkRefreshTokenLifetime,
308
311
  onRefreshStart,
@@ -313,6 +316,38 @@ function useTokenLifecycle(options) {
313
316
  getStorageKey,
314
317
  log
315
318
  ]);
319
+ const shouldRefreshOnResume = useCallback((reason) => {
320
+ const silentRenewEnabled = config.silentRenew?.enabled !== false;
321
+ const refreshOnResume = config.silentRenew?.refreshOnResume !== false;
322
+ const strategy = config.silentRenew?.refreshStrategy || "both";
323
+ const strategyAllowsResume = strategy === "startup" || strategy === "both";
324
+ const hasRefreshToken = Boolean(auth.user?.refresh_token);
325
+ if (!silentRenewEnabled || !refreshOnResume || !strategyAllowsResume) {
326
+ return false;
327
+ }
328
+ if (auth.isLoading) {
329
+ return false;
330
+ }
331
+ if (!auth.user) {
332
+ return false;
333
+ }
334
+ if (supportsRefreshTokens && !hasRefreshToken) {
335
+ log("Skipping resume token check because no refresh token is available:", reason);
336
+ return false;
337
+ }
338
+ if (reason !== "online" && typeof document !== "undefined" && document.visibilityState === "hidden") {
339
+ return false;
340
+ }
341
+ return true;
342
+ }, [
343
+ auth.isLoading,
344
+ auth.user,
345
+ config.silentRenew?.enabled,
346
+ config.silentRenew?.refreshOnResume,
347
+ config.silentRenew?.refreshStrategy,
348
+ supportsRefreshTokens,
349
+ log
350
+ ]);
316
351
  const checkAndRefreshTokens = useCallback(async () => {
317
352
  if (!auth.user) {
318
353
  log("No user session, skipping token check");
@@ -460,6 +495,38 @@ function useTokenLifecycle(options) {
460
495
  checkAndRefreshTokens,
461
496
  log
462
497
  ]);
498
+ useEffect(() => {
499
+ if (typeof window === "undefined" || typeof document === "undefined") {
500
+ return;
501
+ }
502
+ const runResumeCheck = (reason) => {
503
+ if (!shouldRefreshOnResume(reason)) {
504
+ return;
505
+ }
506
+ log("Running resume token check after:", reason);
507
+ checkAndRefreshTokens().catch((error) => {
508
+ log("Resume token check failed:", error);
509
+ });
510
+ };
511
+ const handleVisibilityChange = () => {
512
+ if (document.visibilityState === "visible") {
513
+ runResumeCheck("visibilitychange");
514
+ }
515
+ };
516
+ const handlePageShow = () => runResumeCheck("pageshow");
517
+ const handleFocus = () => runResumeCheck("focus");
518
+ const handleOnline = () => runResumeCheck("online");
519
+ document.addEventListener("visibilitychange", handleVisibilityChange);
520
+ window.addEventListener("pageshow", handlePageShow);
521
+ window.addEventListener("focus", handleFocus);
522
+ window.addEventListener("online", handleOnline);
523
+ return () => {
524
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
525
+ window.removeEventListener("pageshow", handlePageShow);
526
+ window.removeEventListener("focus", handleFocus);
527
+ window.removeEventListener("online", handleOnline);
528
+ };
529
+ }, [checkAndRefreshTokens, log, shouldRefreshOnResume]);
463
530
  useEffect(() => {
464
531
  if (hasCheckedOnStartup.current) {
465
532
  const hasUser = Boolean(auth.user);
@@ -882,7 +949,8 @@ var CONFIG_PRESETS = {
882
949
  maxRefreshTokenLifetime: 2592e3,
883
950
  // 30 days
884
951
  showLoadingIndicator: false,
885
- loadingMessage: "Refreshing session..."
952
+ loadingMessage: "Refreshing session...",
953
+ refreshOnResume: true
886
954
  },
887
955
  advanced: {
888
956
  responseType: "code",
@@ -918,7 +986,8 @@ var CONFIG_PRESETS = {
918
986
  // 30 days
919
987
  showLoadingIndicator: true,
920
988
  // Show loading for better UX
921
- loadingMessage: "Restoring your session..."
989
+ loadingMessage: "Restoring your session...",
990
+ refreshOnResume: true
922
991
  },
923
992
  advanced: {
924
993
  responseType: "code",
@@ -955,7 +1024,8 @@ var CONFIG_PRESETS = {
955
1024
  // 24 hours only
956
1025
  showLoadingIndicator: false,
957
1026
  // Silent for security
958
- loadingMessage: "Validating session..."
1027
+ loadingMessage: "Validating session...",
1028
+ refreshOnResume: true
959
1029
  },
960
1030
  advanced: {
961
1031
  responseType: "code",
@@ -989,7 +1059,8 @@ var CONFIG_PRESETS = {
989
1059
  maxRefreshTokenLifetime: 604800,
990
1060
  // 7 days
991
1061
  showLoadingIndicator: false,
992
- loadingMessage: "Refreshing session..."
1062
+ loadingMessage: "Refreshing session...",
1063
+ refreshOnResume: true
993
1064
  },
994
1065
  advanced: {
995
1066
  responseType: "code",
@@ -1026,7 +1097,8 @@ var CONFIG_PRESETS = {
1026
1097
  // No limit (for convenience)
1027
1098
  showLoadingIndicator: true,
1028
1099
  // Show for debugging
1029
- loadingMessage: "[DEV] Refreshing tokens..."
1100
+ loadingMessage: "[DEV] Refreshing tokens...",
1101
+ refreshOnResume: true
1030
1102
  },
1031
1103
  advanced: {
1032
1104
  responseType: "code",
@@ -1920,6 +1992,59 @@ function useProtectedFetch(options = {}) {
1920
1992
  };
1921
1993
  }
1922
1994
 
1995
+ // src/utils/storedOidcSession.ts
1996
+ var getOidcUserStorageKey = (authority, clientId) => {
1997
+ if (!authority || !clientId) {
1998
+ return null;
1999
+ }
2000
+ return `oidc.user:${authority}:${clientId}`;
2001
+ };
2002
+ var getStoredOidcUser = ({
2003
+ authority,
2004
+ clientId,
2005
+ storage
2006
+ }) => {
2007
+ const storageKey = getOidcUserStorageKey(authority, clientId);
2008
+ if (!storageKey || !storage) {
2009
+ return null;
2010
+ }
2011
+ try {
2012
+ const storedUser = storage.getItem(storageKey);
2013
+ if (!storedUser) {
2014
+ return null;
2015
+ }
2016
+ return JSON.parse(storedUser);
2017
+ } catch {
2018
+ return null;
2019
+ }
2020
+ };
2021
+ var hasStoredOidcRefreshToken = (args) => {
2022
+ const storedUser = getStoredOidcUser(args);
2023
+ return typeof storedUser?.refresh_token === "string" && storedUser.refresh_token.length > 0;
2024
+ };
2025
+ var getStoredOidcSession = (args) => {
2026
+ const storageKey = getOidcUserStorageKey(args.authority, args.clientId);
2027
+ const storedUser = getStoredOidcUser(args);
2028
+ return {
2029
+ storageKey,
2030
+ storedUser,
2031
+ hasStoredUser: storedUser !== null,
2032
+ hasRefreshToken: typeof storedUser?.refresh_token === "string" && storedUser.refresh_token.length > 0
2033
+ };
2034
+ };
2035
+
2036
+ // src/hooks/useStoredOidcSession.ts
2037
+ var useStoredOidcSession = () => {
2038
+ const auth = useAuth();
2039
+ const { storage } = useStorageConfig();
2040
+ const authority = auth.settings.authority;
2041
+ const clientId = auth.settings.client_id;
2042
+ return useMemo(
2043
+ () => getStoredOidcSession({ authority, clientId, storage }),
2044
+ [authority, clientId, storage]
2045
+ );
2046
+ };
2047
+
1923
2048
  // node_modules/@microsoft/signalr/dist/esm/Errors.js
1924
2049
  var HttpError = class extends Error {
1925
2050
  /** Constructs a new instance of {@link @microsoft/signalr.HttpError}.
@@ -4958,6 +5083,6 @@ function useSignalRConnection(hubUrl, options = {}) {
4958
5083
  };
4959
5084
  }
4960
5085
 
4961
- export { CONFIG_PRESETS, DeviceLogin, KeycloakConfigBuilder, KeycloakProvider, KeycloakStatusProvider, LoginButton, LogoutButton, ProtectedRoute, SessionMonitor, TokenBridge, TokenBridgeSync, TokenLifecycleManager, UserProfile, createTokenBridge, defaultConfig, defaultTokenBridge, fetchUserInfo, getConfigFromEnv, getConfigPreset, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useKeycloakStatus, useProtectedFetch, useRoles, useScopes, useSignalRConnection, useTokenLifecycle };
5086
+ export { CONFIG_PRESETS, DeviceLogin, KeycloakConfigBuilder, KeycloakProvider, KeycloakStatusProvider, LoginButton, LogoutButton, ProtectedRoute, SessionMonitor, TokenBridge, TokenBridgeSync, TokenLifecycleManager, UserProfile, createTokenBridge, defaultConfig, defaultTokenBridge, fetchUserInfo, getConfigFromEnv, getConfigPreset, getOidcUserStorageKey, getStoredOidcSession, getStoredOidcUser, hasStoredOidcRefreshToken, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useKeycloakStatus, useProtectedFetch, useRoles, useScopes, useSignalRConnection, useStoredOidcSession, useTokenLifecycle };
4962
5087
  //# sourceMappingURL=index.mjs.map
4963
5088
  //# sourceMappingURL=index.mjs.map