antaeus.keycloak.react 2.2.3 → 2.4.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.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import React from 'react';
1
+ import * as React$1 from 'react';
2
+ import React__default from 'react';
2
3
  import { User, UserManagerSettings } from 'oidc-client-ts';
3
4
  export { User } from 'oidc-client-ts';
4
5
  import { useLocation } from 'react-router-dom';
@@ -113,6 +114,12 @@ interface SilentRenewConfig {
113
114
  * @default 'Refreshing session...'
114
115
  */
115
116
  loadingMessage?: string;
117
+ /**
118
+ * Enable using the refresh token grant instead of iframe silent renew when available.
119
+ * Keeps long-lived sessions alive even after the app is closed.
120
+ * @default true
121
+ */
122
+ useRefreshToken?: boolean;
116
123
  }
117
124
  /**
118
125
  * Event callbacks for authentication lifecycle
@@ -217,6 +224,40 @@ declare function getConfigPreset(preset: ConfigPreset): Partial<KeycloakConfig>;
217
224
  */
218
225
  declare function mergeWithPreset(preset: ConfigPreset, customConfig: Partial<KeycloakConfig>): Partial<KeycloakConfig>;
219
226
 
227
+ type TokenListener = (token: string | null) => void;
228
+ /**
229
+ * Minimal bridge to expose the current Keycloak access token outside React.
230
+ * Consumers can subscribe to token changes or fetch the latest value on demand.
231
+ */
232
+ declare class TokenBridge {
233
+ private currentToken;
234
+ private listeners;
235
+ /**
236
+ * Update the tracked token and notify subscribers when it changes.
237
+ */
238
+ setToken(token: string | null): void;
239
+ /**
240
+ * Read the most recent token value.
241
+ */
242
+ getToken(): string | null;
243
+ /**
244
+ * Convenience helper for async callers.
245
+ */
246
+ getTokenAsync(): Promise<string | null>;
247
+ /**
248
+ * Subscribe to token changes. Returns an unsubscribe callback.
249
+ */
250
+ subscribe(listener: TokenListener): () => void;
251
+ }
252
+ /**
253
+ * Factory helper to create an isolated token bridge.
254
+ */
255
+ declare const createTokenBridge: () => TokenBridge;
256
+ /**
257
+ * Default shared bridge for simple scenarios.
258
+ */
259
+ declare const defaultTokenBridge: TokenBridge;
260
+
220
261
  /**
221
262
  * Props for zero-config mode (simplified setup)
222
263
  */
@@ -234,10 +275,16 @@ interface KeycloakProviderZeroConfigProps {
234
275
  * @default 'default'
235
276
  */
236
277
  preset?: ConfigPreset;
278
+ /**
279
+ * Toggle to disable Keycloak integration entirely.
280
+ * When false, children render with a stub auth context.
281
+ * @default true
282
+ */
283
+ enabled?: boolean;
237
284
  /**
238
285
  * Child components to render
239
286
  */
240
- children: React.ReactNode;
287
+ children: React__default.ReactNode;
241
288
  /**
242
289
  * Enable debug logging to console
243
290
  * @default false
@@ -248,6 +295,10 @@ interface KeycloakProviderZeroConfigProps {
248
295
  * Use this to customize preset or zero-config defaults
249
296
  */
250
297
  config?: Partial<Omit<KeycloakConfig, 'authority' | 'clientId'>>;
298
+ /**
299
+ * Optional token bridge instance for exposing tokens outside React.
300
+ */
301
+ tokenBridge?: TokenBridge;
251
302
  }
252
303
  /**
253
304
  * Props for full-config mode (advanced setup)
@@ -257,15 +308,25 @@ interface KeycloakProviderFullConfigProps {
257
308
  * Full Keycloak configuration object
258
309
  */
259
310
  config: KeycloakConfig;
311
+ /**
312
+ * Toggle to disable Keycloak integration entirely.
313
+ * When false, children render with a stub auth context.
314
+ * @default true
315
+ */
316
+ enabled?: boolean;
260
317
  /**
261
318
  * Child components to render
262
319
  */
263
- children: React.ReactNode;
320
+ children: React__default.ReactNode;
264
321
  /**
265
322
  * Enable debug logging to console
266
323
  * @default false
267
324
  */
268
325
  debug?: boolean;
326
+ /**
327
+ * Optional token bridge instance for exposing tokens outside React.
328
+ */
329
+ tokenBridge?: TokenBridge;
269
330
  }
270
331
  /**
271
332
  * Combined props type
@@ -318,6 +379,7 @@ type KeycloakProviderProps = KeycloakProviderZeroConfigProps | KeycloakProviderF
318
379
  * silentRenew: {
319
380
  * maxRefreshTokenLifetime: 604800, // Override: 7 days instead of 30
320
381
  * showLoadingIndicator: true,
382
+ * useRefreshToken: true, // Explicitly ensure refresh tokens are used when available
321
383
  * }
322
384
  * }}
323
385
  * debug={true}
@@ -361,6 +423,7 @@ type KeycloakProviderProps = KeycloakProviderZeroConfigProps | KeycloakProviderF
361
423
  * enabled: true,
362
424
  * refreshStrategy: 'both',
363
425
  * maxRefreshTokenLifetime: 2592000,
426
+ * useRefreshToken: true,
364
427
  * },
365
428
  * events: {
366
429
  * onLogin: (user) => console.log('User logged in', user),
@@ -380,6 +443,7 @@ type KeycloakProviderProps = KeycloakProviderZeroConfigProps | KeycloakProviderF
380
443
  * - ✅ Token lifecycle management (refresh on app startup)
381
444
  * - ✅ Session monitoring UI (countdown timer)
382
445
  * - ✅ Silent renew (background token refresh)
446
+ * - ✅ Refresh token grant fallback on app restart
383
447
  * - ✅ Error notifications (user-friendly messages)
384
448
  * - ✅ 401 retry logic (automatic token refresh on API errors)
385
449
  * - ✅ Maximum refresh token lifetime (security control)
@@ -387,7 +451,7 @@ type KeycloakProviderProps = KeycloakProviderZeroConfigProps | KeycloakProviderF
387
451
  *
388
452
  * All features can be disabled or customized via configuration overrides.
389
453
  */
390
- declare const KeycloakProvider: React.FC<KeycloakProviderProps>;
454
+ declare const KeycloakProvider: React__default.FC<KeycloakProviderProps>;
391
455
 
392
456
  /**
393
457
  * Context object passed to custom policy function
@@ -410,7 +474,7 @@ interface ProtectedRouteProps {
410
474
  /**
411
475
  * Child components to render when user is authorized
412
476
  */
413
- children: React.ReactNode;
477
+ children: React__default.ReactNode;
414
478
  /**
415
479
  * Required realm roles (OR logic - user needs at least one)
416
480
  */
@@ -437,11 +501,11 @@ interface ProtectedRouteProps {
437
501
  /**
438
502
  * Custom loading component to display while checking authentication
439
503
  */
440
- loadingComponent?: React.ReactNode;
504
+ loadingComponent?: React__default.ReactNode;
441
505
  /**
442
506
  * Custom unauthorized component to display when user lacks required permissions
443
507
  */
444
- unauthorizedComponent?: React.ReactNode;
508
+ unauthorizedComponent?: React__default.ReactNode;
445
509
  /**
446
510
  * Path to redirect to if not authenticated
447
511
  * @default '/login'
@@ -568,14 +632,14 @@ interface ProtectedRouteProps {
568
632
  * </ProtectedRoute>
569
633
  * ```
570
634
  */
571
- declare const ProtectedRoute: React.FC<ProtectedRouteProps>;
635
+ declare const ProtectedRoute: React__default.FC<ProtectedRouteProps>;
572
636
 
573
637
  interface LoginButtonProps {
574
638
  /**
575
639
  * Button text
576
640
  * @default 'Login'
577
641
  */
578
- children?: React.ReactNode;
642
+ children?: React__default.ReactNode;
579
643
  /**
580
644
  * Additional CSS class names
581
645
  */
@@ -602,7 +666,7 @@ interface LoginButtonProps {
602
666
  /**
603
667
  * Custom inline styles
604
668
  */
605
- style?: React.CSSProperties;
669
+ style?: React__default.CSSProperties;
606
670
  /**
607
671
  * Disable the button
608
672
  */
@@ -642,14 +706,14 @@ interface LoginButtonProps {
642
706
  * />
643
707
  * ```
644
708
  */
645
- declare const LoginButton: React.FC<LoginButtonProps>;
709
+ declare const LoginButton: React__default.FC<LoginButtonProps>;
646
710
 
647
711
  interface LogoutButtonProps {
648
712
  /**
649
713
  * Button text
650
714
  * @default 'Logout'
651
715
  */
652
- children?: React.ReactNode;
716
+ children?: React__default.ReactNode;
653
717
  /**
654
718
  * Additional CSS class names
655
719
  */
@@ -676,7 +740,7 @@ interface LogoutButtonProps {
676
740
  /**
677
741
  * Custom inline styles
678
742
  */
679
- style?: React.CSSProperties;
743
+ style?: React__default.CSSProperties;
680
744
  /**
681
745
  * Disable the button
682
746
  */
@@ -719,7 +783,7 @@ interface LogoutButtonProps {
719
783
  * />
720
784
  * ```
721
785
  */
722
- declare const LogoutButton: React.FC<LogoutButtonProps>;
786
+ declare const LogoutButton: React__default.FC<LogoutButtonProps>;
723
787
 
724
788
  interface UserProfileProps {
725
789
  /**
@@ -749,7 +813,7 @@ interface UserProfileProps {
749
813
  /**
750
814
  * Custom inline styles
751
815
  */
752
- style?: React.CSSProperties;
816
+ style?: React__default.CSSProperties;
753
817
  /**
754
818
  * Callback when profile is clicked
755
819
  */
@@ -793,7 +857,7 @@ interface UserProfileProps {
793
857
  * />
794
858
  * ```
795
859
  */
796
- declare const UserProfile: React.FC<UserProfileProps>;
860
+ declare const UserProfile: React__default.FC<UserProfileProps>;
797
861
 
798
862
  interface SessionMonitorProps {
799
863
  /**
@@ -845,7 +909,7 @@ interface SessionMonitorProps {
845
909
  /**
846
910
  * Custom inline styles
847
911
  */
848
- style?: React.CSSProperties;
912
+ style?: React__default.CSSProperties;
849
913
  }
850
914
  /**
851
915
  * Session monitoring component with countdown timer
@@ -888,7 +952,7 @@ interface SessionMonitorProps {
888
952
  * />
889
953
  * ```
890
954
  */
891
- declare const SessionMonitor: React.FC<SessionMonitorProps>;
955
+ declare const SessionMonitor: React__default.FC<SessionMonitorProps>;
892
956
 
893
957
  interface DeviceLoginProps {
894
958
  /**
@@ -921,7 +985,7 @@ interface DeviceLoginProps {
921
985
  /**
922
986
  * Custom inline styles
923
987
  */
924
- style?: React.CSSProperties;
988
+ style?: React__default.CSSProperties;
925
989
  }
926
990
  /**
927
991
  * Device flow login component
@@ -956,7 +1020,7 @@ interface DeviceLoginProps {
956
1020
  * />
957
1021
  * ```
958
1022
  */
959
- declare const DeviceLogin: React.FC<DeviceLoginProps>;
1023
+ declare const DeviceLogin: React__default.FC<DeviceLoginProps>;
960
1024
 
961
1025
  interface TokenLifecycleManagerProps {
962
1026
  /**
@@ -966,7 +1030,7 @@ interface TokenLifecycleManagerProps {
966
1030
  /**
967
1031
  * Child components to render
968
1032
  */
969
- children: React.ReactNode;
1033
+ children: React__default.ReactNode;
970
1034
  /**
971
1035
  * Enable debug logging
972
1036
  * @default false
@@ -988,7 +1052,19 @@ interface TokenLifecycleManagerProps {
988
1052
  * </TokenLifecycleManager>
989
1053
  * ```
990
1054
  */
991
- declare const TokenLifecycleManager: React.FC<TokenLifecycleManagerProps>;
1055
+ declare const TokenLifecycleManager: React__default.FC<TokenLifecycleManagerProps>;
1056
+
1057
+ interface TokenBridgeProps {
1058
+ /**
1059
+ * Optional custom bridge instance. Defaults to the shared global bridge.
1060
+ */
1061
+ bridge?: TokenBridge;
1062
+ }
1063
+ /**
1064
+ * Synchronises the current Keycloak access token into an imperative TokenBridge.
1065
+ * Place this component inside your application tree (after KeycloakProvider).
1066
+ */
1067
+ declare const TokenBridgeSync: React.FC<TokenBridgeProps>;
992
1068
 
993
1069
  /**
994
1070
  * Main authentication hook for accessing Keycloak authentication state and methods
@@ -1397,6 +1473,24 @@ declare class KeycloakConfigBuilder {
1397
1473
  */
1398
1474
  declare function getConfigFromEnv(): Partial<KeycloakConfig>;
1399
1475
 
1476
+ type KeycloakDisabledReason = 'disabled' | 'missing-config';
1477
+ interface KeycloakStatus {
1478
+ /**
1479
+ * Whether the Keycloak integration is enabled by configuration.
1480
+ */
1481
+ isEnabled: boolean;
1482
+ /**
1483
+ * Whether the minimum configuration (authority + clientId) is present.
1484
+ */
1485
+ isConfigured: boolean;
1486
+ /**
1487
+ * Provides additional context when integration is disabled.
1488
+ */
1489
+ reason?: KeycloakDisabledReason;
1490
+ }
1491
+ declare const KeycloakStatusProvider: React$1.Provider<KeycloakStatus>;
1492
+ declare const useKeycloakStatus: () => KeycloakStatus;
1493
+
1400
1494
  /**
1401
1495
  * Device authorization response from Keycloak
1402
1496
  */
@@ -1508,4 +1602,4 @@ declare function pollForToken(apiBaseUrl: string, deviceCode: string): Promise<T
1508
1602
  */
1509
1603
  declare function fetchUserInfo(apiBaseUrl: string, accessToken: string): Promise<any>;
1510
1604
 
1511
- export { type AdvancedOidcOptions, CONFIG_PRESETS, type ConfigPreset, type DeviceAuthorizationResponse, type DeviceFlowError, DeviceLogin, type DeviceLoginProps, type EventCallbacks, type FeatureFlags, type KeycloakConfig, KeycloakConfigBuilder, KeycloakProvider, type KeycloakProviderFullConfigProps, type KeycloakProviderProps, type KeycloakProviderZeroConfigProps, LoginButton, type LoginButtonProps, LogoutButton, type LogoutButtonProps, type PolicyContext, type PolicyFunction, ProtectedRoute, type ProtectedRouteProps, SessionMonitor, type SessionMonitorProps, type SilentRenewConfig, TokenLifecycleManager, type TokenLifecycleManagerProps, type TokenResponse, type UseProtectedFetchOptions, type UseProtectedFetchReturn, type UseRolesReturn, type UseScopesReturn, type UseTokenLifecycleOptions, type UseTokenLifecycleReturn, UserProfile, type UserProfileProps, defaultConfig, fetchUserInfo, getConfigFromEnv, getConfigPreset, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useProtectedFetch, useRoles, useScopes, useTokenLifecycle };
1605
+ export { type AdvancedOidcOptions, CONFIG_PRESETS, type ConfigPreset, type DeviceAuthorizationResponse, type DeviceFlowError, DeviceLogin, type DeviceLoginProps, type EventCallbacks, type FeatureFlags, type KeycloakConfig, KeycloakConfigBuilder, type KeycloakDisabledReason, KeycloakProvider, type KeycloakProviderFullConfigProps, type KeycloakProviderProps, type KeycloakProviderZeroConfigProps, type KeycloakStatus, KeycloakStatusProvider, LoginButton, type LoginButtonProps, LogoutButton, type LogoutButtonProps, type PolicyContext, type PolicyFunction, ProtectedRoute, type ProtectedRouteProps, SessionMonitor, type SessionMonitorProps, type SilentRenewConfig, TokenBridge, type TokenBridgeProps, TokenBridgeSync, TokenLifecycleManager, type TokenLifecycleManagerProps, type TokenListener, type TokenResponse, type UseProtectedFetchOptions, type UseProtectedFetchReturn, type UseRolesReturn, type UseScopesReturn, type UseTokenLifecycleOptions, type UseTokenLifecycleReturn, UserProfile, type UserProfileProps, createTokenBridge, defaultConfig, defaultTokenBridge, fetchUserInfo, getConfigFromEnv, getConfigPreset, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useKeycloakStatus, useProtectedFetch, useRoles, useScopes, useTokenLifecycle };
package/dist/index.js CHANGED
@@ -30,7 +30,8 @@ var defaultConfig = {
30
30
  maxRefreshTokenLifetime: 2592e3,
31
31
  // 30 days in seconds
32
32
  showLoadingIndicator: false,
33
- loadingMessage: "Refreshing session..."
33
+ loadingMessage: "Refreshing session...",
34
+ useRefreshToken: true
34
35
  },
35
36
  advanced: {
36
37
  responseType: "code",
@@ -56,6 +57,7 @@ var KeycloakConfigBuilder = class {
56
57
  const config = this.mergeWithDefaults(userConfig);
57
58
  const storageType = config.advanced.storageType || "session";
58
59
  const storage = storageType === "local" ? window.localStorage : window.sessionStorage;
60
+ const { useRefreshToken } = config.silentRenew;
59
61
  const oidcSettings = {
60
62
  authority: config.authority,
61
63
  client_id: config.clientId,
@@ -73,6 +75,9 @@ var KeycloakConfigBuilder = class {
73
75
  response_mode: "query"
74
76
  }
75
77
  };
78
+ if (useRefreshToken !== void 0) {
79
+ oidcSettings.useRefreshToken = useRefreshToken;
80
+ }
76
81
  return oidcSettings;
77
82
  }
78
83
  /**
@@ -160,6 +165,7 @@ function useTokenLifecycle(options) {
160
165
  const [isRefreshing, setIsRefreshing] = react.useState(false);
161
166
  const [isRefreshTokenExpired, setIsRefreshTokenExpired] = react.useState(false);
162
167
  const hasCheckedOnStartup = react.useRef(false);
168
+ const supportsRefreshTokens = config.silentRenew?.useRefreshToken !== false;
163
169
  const log = react.useCallback(
164
170
  (message, ...args) => {
165
171
  if (debug) {
@@ -296,24 +302,49 @@ function useTokenLifecycle(options) {
296
302
  react.useEffect(() => {
297
303
  const strategy = config.silentRenew?.refreshStrategy || "both";
298
304
  const shouldCheckOnStartup = strategy === "startup" || strategy === "both";
305
+ const hasRefreshToken = Boolean(auth.user?.refresh_token);
299
306
  if (!shouldCheckOnStartup || hasCheckedOnStartup.current) {
300
307
  return;
301
308
  }
302
- if (!auth.isAuthenticated || auth.isLoading) {
309
+ if (auth.isLoading) {
303
310
  return;
304
311
  }
312
+ if (!auth.isAuthenticated) {
313
+ if (!supportsRefreshTokens) {
314
+ return;
315
+ }
316
+ if (!auth.user || !hasRefreshToken) {
317
+ return;
318
+ }
319
+ }
305
320
  hasCheckedOnStartup.current = true;
306
- log("Running startup token check with strategy:", strategy);
321
+ log(
322
+ "Running startup token check with strategy:",
323
+ strategy,
324
+ "using refresh token:",
325
+ !auth.isAuthenticated && supportsRefreshTokens
326
+ );
307
327
  checkAndRefreshTokens().catch((error) => {
308
328
  log("Startup token check failed:", error);
309
329
  });
310
330
  }, [
311
331
  auth.isAuthenticated,
312
332
  auth.isLoading,
333
+ auth.user,
313
334
  config.silentRenew?.refreshStrategy,
335
+ supportsRefreshTokens,
314
336
  checkAndRefreshTokens,
315
337
  log
316
338
  ]);
339
+ react.useEffect(() => {
340
+ if (hasCheckedOnStartup.current) {
341
+ const hasUser = Boolean(auth.user);
342
+ const hasRefreshToken = Boolean(auth.user?.refresh_token);
343
+ if (auth.isLoading || !auth.isAuthenticated && (!supportsRefreshTokens || !hasUser || !hasRefreshToken)) {
344
+ hasCheckedOnStartup.current = false;
345
+ }
346
+ }
347
+ }, [auth.isAuthenticated, auth.isLoading, auth.user, supportsRefreshTokens]);
317
348
  react.useEffect(() => {
318
349
  if (auth.user && !auth.isLoading) {
319
350
  storeFirstLoginTime();
@@ -628,6 +659,71 @@ var SessionMonitor = ({
628
659
  ] });
629
660
  };
630
661
 
662
+ // src/utils/tokenBridge.ts
663
+ var TokenBridge = class {
664
+ constructor() {
665
+ this.currentToken = null;
666
+ this.listeners = /* @__PURE__ */ new Set();
667
+ }
668
+ /**
669
+ * Update the tracked token and notify subscribers when it changes.
670
+ */
671
+ setToken(token) {
672
+ if (this.currentToken === token) {
673
+ return;
674
+ }
675
+ this.currentToken = token;
676
+ for (const listener of this.listeners) {
677
+ try {
678
+ listener(token);
679
+ } catch (error) {
680
+ console.error("[TokenBridge] Listener threw error:", error);
681
+ }
682
+ }
683
+ }
684
+ /**
685
+ * Read the most recent token value.
686
+ */
687
+ getToken() {
688
+ return this.currentToken;
689
+ }
690
+ /**
691
+ * Convenience helper for async callers.
692
+ */
693
+ async getTokenAsync() {
694
+ return this.currentToken;
695
+ }
696
+ /**
697
+ * Subscribe to token changes. Returns an unsubscribe callback.
698
+ */
699
+ subscribe(listener) {
700
+ this.listeners.add(listener);
701
+ listener(this.currentToken);
702
+ return () => {
703
+ this.listeners.delete(listener);
704
+ };
705
+ }
706
+ };
707
+ var createTokenBridge = () => new TokenBridge();
708
+ var defaultTokenBridge = createTokenBridge();
709
+
710
+ // src/components/TokenBridge.tsx
711
+ var TokenBridgeSync = ({
712
+ bridge = defaultTokenBridge
713
+ }) => {
714
+ const auth = useAuth();
715
+ react.useEffect(() => {
716
+ const token = auth.user?.access_token ?? null;
717
+ bridge.setToken(token);
718
+ }, [auth.user, bridge]);
719
+ react.useEffect(() => {
720
+ if (!auth.isAuthenticated) {
721
+ bridge.setToken(null);
722
+ }
723
+ }, [auth.isAuthenticated, bridge]);
724
+ return null;
725
+ };
726
+
631
727
  // src/config/presets.ts
632
728
  var CONFIG_PRESETS = {
633
729
  /**
@@ -846,11 +942,97 @@ var StorageConfigProvider = ({ storageType, children }) => {
846
942
  return /* @__PURE__ */ jsxRuntime.jsx(StorageConfigContext.Provider, { value: { storageType, storage }, children });
847
943
  };
848
944
  var useStorageConfig = () => react.useContext(StorageConfigContext);
945
+ var defaultStatus = {
946
+ isEnabled: true,
947
+ isConfigured: true
948
+ };
949
+ var KeycloakStatusContext = react.createContext(defaultStatus);
950
+ var KeycloakStatusProvider = KeycloakStatusContext.Provider;
951
+ var useKeycloakStatus = () => react.useContext(KeycloakStatusContext);
952
+ var createDisabledEvents = () => {
953
+ const noop = () => void 0;
954
+ const noopSubscription = () => () => void 0;
955
+ const noopAsync = async () => void 0;
956
+ const events = {
957
+ addAccessTokenExpiring: noopSubscription,
958
+ removeAccessTokenExpiring: noop,
959
+ addAccessTokenExpired: noopSubscription,
960
+ removeAccessTokenExpired: noop,
961
+ addSilentRenewError: noopSubscription,
962
+ removeSilentRenewError: noop,
963
+ addUserLoaded: noopSubscription,
964
+ removeUserLoaded: noop,
965
+ addUserUnloaded: noopSubscription,
966
+ removeUserUnloaded: noop,
967
+ addUserSignedIn: noopSubscription,
968
+ removeUserSignedIn: noop,
969
+ addUserSignedOut: noopSubscription,
970
+ removeUserSignedOut: noop,
971
+ addUserSessionChanged: noopSubscription,
972
+ removeUserSessionChanged: noop,
973
+ load: noopAsync,
974
+ unload: noopAsync
975
+ };
976
+ return events;
977
+ };
978
+ var disabledEvents = createDisabledEvents();
979
+ var disabledSettings = {
980
+ authority: "",
981
+ client_id: "",
982
+ redirect_uri: "http://localhost/disabled",
983
+ response_type: "code",
984
+ scope: ""
985
+ };
986
+ var disabledError = () => new Error("Keycloak integration is disabled for this environment.");
987
+ var disabledAsync = async () => {
988
+ throw disabledError();
989
+ };
990
+ var disabledAuthContext = {
991
+ settings: disabledSettings,
992
+ events: disabledEvents,
993
+ clearStaleState: async () => void 0,
994
+ removeUser: async () => void 0,
995
+ signinPopup: (_args) => disabledAsync(),
996
+ signinSilent: async (_args) => null,
997
+ signinRedirect: (_args) => disabledAsync(),
998
+ signinResourceOwnerCredentials: (_args) => disabledAsync(),
999
+ signoutRedirect: async (_args) => void 0,
1000
+ signoutPopup: async (_args) => void 0,
1001
+ signoutSilent: async (_args) => void 0,
1002
+ querySessionStatus: async (_args) => null,
1003
+ revokeTokens: async (_types) => void 0,
1004
+ startSilentRenew: () => void 0,
1005
+ stopSilentRenew: () => void 0,
1006
+ user: null,
1007
+ isLoading: false,
1008
+ isAuthenticated: false,
1009
+ activeNavigator: void 0,
1010
+ error: void 0
1011
+ };
849
1012
  function isZeroConfig(props) {
850
1013
  return "authority" in props && "clientId" in props;
851
1014
  }
852
1015
  var KeycloakProvider = (props) => {
853
1016
  const { children, debug = false } = props;
1017
+ const status = react.useMemo(() => {
1018
+ const enabledFlag = props.enabled ?? true;
1019
+ const hasAuthority = isZeroConfig(props) ? Boolean(props.authority) : Boolean(props.config.authority);
1020
+ const hasClientId = isZeroConfig(props) ? Boolean(props.clientId) : Boolean(props.config.clientId);
1021
+ return {
1022
+ isEnabled: enabledFlag,
1023
+ isConfigured: hasAuthority && hasClientId,
1024
+ reason: !enabledFlag ? "disabled" : !hasAuthority || !hasClientId ? "missing-config" : void 0
1025
+ };
1026
+ }, [props]);
1027
+ const effectiveEnabled = status.isEnabled && status.isConfigured;
1028
+ react.useEffect(() => {
1029
+ if (!effectiveEnabled && props.tokenBridge) {
1030
+ props.tokenBridge.setToken(null);
1031
+ }
1032
+ }, [effectiveEnabled, props.tokenBridge]);
1033
+ if (!effectiveEnabled) {
1034
+ return /* @__PURE__ */ jsxRuntime.jsx(KeycloakStatusProvider, { value: status, children: /* @__PURE__ */ jsxRuntime.jsx(reactOidcContext.AuthContext.Provider, { value: disabledAuthContext, children: /* @__PURE__ */ jsxRuntime.jsx(StorageConfigProvider, { storageType: "session", children }) }) });
1035
+ }
854
1036
  const config = isZeroConfig(props) ? buildZeroConfig(props) : props.config;
855
1037
  const oidcConfig = KeycloakConfigBuilder.build(config);
856
1038
  const log = react.useCallback(
@@ -892,7 +1074,7 @@ var KeycloakProvider = (props) => {
892
1074
  }, [log]);
893
1075
  const showSessionMonitor = config.features?.sessionMonitor !== false;
894
1076
  const storageType = config.advanced?.storageType || "session";
895
- return /* @__PURE__ */ jsxRuntime.jsx(
1077
+ return /* @__PURE__ */ jsxRuntime.jsx(KeycloakStatusProvider, { value: status, children: /* @__PURE__ */ jsxRuntime.jsx(
896
1078
  reactOidcContext.AuthProvider,
897
1079
  {
898
1080
  ...oidcConfig,
@@ -900,11 +1082,12 @@ var KeycloakProvider = (props) => {
900
1082
  onSignoutCallback,
901
1083
  onRemoveUser,
902
1084
  children: /* @__PURE__ */ jsxRuntime.jsx(StorageConfigProvider, { storageType, children: /* @__PURE__ */ jsxRuntime.jsxs(TokenLifecycleManager, { config, debug, children: [
1085
+ props.tokenBridge && /* @__PURE__ */ jsxRuntime.jsx(TokenBridgeSync, { bridge: props.tokenBridge }),
903
1086
  showSessionMonitor && /* @__PURE__ */ jsxRuntime.jsx(SessionMonitor, {}),
904
1087
  children
905
1088
  ] }) })
906
1089
  }
907
- );
1090
+ ) });
908
1091
  };
909
1092
  function buildZeroConfig(props) {
910
1093
  const { authority, clientId, preset = "default", config: overrides = {} } = props;
@@ -1612,13 +1795,18 @@ exports.CONFIG_PRESETS = CONFIG_PRESETS;
1612
1795
  exports.DeviceLogin = DeviceLogin;
1613
1796
  exports.KeycloakConfigBuilder = KeycloakConfigBuilder;
1614
1797
  exports.KeycloakProvider = KeycloakProvider;
1798
+ exports.KeycloakStatusProvider = KeycloakStatusProvider;
1615
1799
  exports.LoginButton = LoginButton;
1616
1800
  exports.LogoutButton = LogoutButton;
1617
1801
  exports.ProtectedRoute = ProtectedRoute;
1618
1802
  exports.SessionMonitor = SessionMonitor;
1803
+ exports.TokenBridge = TokenBridge;
1804
+ exports.TokenBridgeSync = TokenBridgeSync;
1619
1805
  exports.TokenLifecycleManager = TokenLifecycleManager;
1620
1806
  exports.UserProfile = UserProfile;
1807
+ exports.createTokenBridge = createTokenBridge;
1621
1808
  exports.defaultConfig = defaultConfig;
1809
+ exports.defaultTokenBridge = defaultTokenBridge;
1622
1810
  exports.fetchUserInfo = fetchUserInfo;
1623
1811
  exports.getConfigFromEnv = getConfigFromEnv;
1624
1812
  exports.getConfigPreset = getConfigPreset;
@@ -1626,6 +1814,7 @@ exports.mergeWithPreset = mergeWithPreset;
1626
1814
  exports.pollForToken = pollForToken;
1627
1815
  exports.requestDeviceCode = requestDeviceCode;
1628
1816
  exports.useAuth = useAuth;
1817
+ exports.useKeycloakStatus = useKeycloakStatus;
1629
1818
  exports.useProtectedFetch = useProtectedFetch;
1630
1819
  exports.useRoles = useRoles;
1631
1820
  exports.useScopes = useScopes;