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/README.md CHANGED
@@ -115,6 +115,28 @@ function MyComponent() {
115
115
  </KeycloakProvider>
116
116
  ```
117
117
 
118
+ ### 5. Optional / Disabled Mode
119
+
120
+ Wrap legacy paths or feature flags without tearing out the provider:
121
+
122
+ ```tsx
123
+ <KeycloakProvider
124
+ authority={import.meta.env.VITE_KEYCLOAK_AUTHORITY ?? ''}
125
+ clientId={import.meta.env.VITE_KEYCLOAK_CLIENT_ID ?? ''}
126
+ enabled={Boolean(import.meta.env.VITE_KEYCLOAK_AUTHORITY)}
127
+ >
128
+ <App />
129
+ </KeycloakProvider>
130
+ ```
131
+
132
+ When disabled (or missing authority/clientId), the provider exposes a safe stub context—`useAuth()` still works, but no network calls are made. Inspect the state anywhere via:
133
+
134
+ ```tsx
135
+ import { useKeycloakStatus } from 'antaeus.keycloak.react';
136
+
137
+ const { isEnabled, isConfigured, reason } = useKeycloakStatus();
138
+ ```
139
+
118
140
  ## Configuration Presets
119
141
 
120
142
  | Preset | Session Lifetime | Best For |
@@ -144,6 +166,61 @@ auth.signinRedirect()
144
166
  auth.signoutRedirect()
145
167
  ```
146
168
 
169
+ ## Token Bridge (Non-React Consumers)
170
+
171
+ Expose tokens to plain TypeScript modules (SignalR, fetch helpers, etc.):
172
+
173
+ ```tsx
174
+ import {
175
+ KeycloakProvider,
176
+ TokenBridgeSync,
177
+ createTokenBridge,
178
+ } from 'antaeus.keycloak.react';
179
+
180
+ export const tokenBridge = createTokenBridge();
181
+
182
+ ReactDOM.createRoot(document.getElementById('root')!).render(
183
+ <KeycloakProvider authority="..." clientId="..." tokenBridge={tokenBridge}>
184
+ <TokenBridgeSync bridge={tokenBridge} />
185
+ <App />
186
+ </KeycloakProvider>
187
+ );
188
+ ```
189
+
190
+ Then outside React:
191
+
192
+ ```ts
193
+ import { tokenBridge } from './main';
194
+
195
+ tokenBridge.subscribe((token) => {
196
+ console.log('Token changed:', token);
197
+ });
198
+ ```
199
+
200
+ Prefer a shared singleton? Use the default bridge:
201
+
202
+ ```tsx
203
+ import {
204
+ KeycloakProvider,
205
+ TokenBridgeSync,
206
+ defaultTokenBridge,
207
+ } from 'antaeus.keycloak.react';
208
+
209
+ ReactDOM.createRoot(document.getElementById('root')!).render(
210
+ <KeycloakProvider authority="..." clientId="..." tokenBridge={defaultTokenBridge}>
211
+ <TokenBridgeSync />
212
+ <App />
213
+ </KeycloakProvider>
214
+ );
215
+ ```
216
+
217
+ ```ts
218
+ // signalr.ts
219
+ import { defaultTokenBridge } from 'antaeus.keycloak.react';
220
+
221
+ const bearer = defaultTokenBridge.getToken();
222
+ ```
223
+
147
224
  ### useProtectedFetch()
148
225
 
149
226
  ```tsx
@@ -325,6 +402,24 @@ If your refresh token expires (exceeds `maxRefreshTokenLifetime`):
325
402
  />
326
403
  ```
327
404
 
405
+ ### Refresh Tokens After Restart
406
+
407
+ When a refresh token is available the provider now reuses it on startup, ensuring long-lived sessions survive browser reloads or app restarts. The refresh token grant is attempted automatically before falling back to iframe-based silent renew.
408
+
409
+ If you need to disable this behavior and revert to iframe-only silent renew, set:
410
+
411
+ ```tsx
412
+ <KeycloakProvider
413
+ authority="..."
414
+ clientId="my-app"
415
+ config={{
416
+ silentRenew: {
417
+ useRefreshToken: false,
418
+ },
419
+ }}
420
+ />
421
+ ```
422
+
328
423
  ### Startup Token Refresh
329
424
 
330
425
  When your app loads with an existing session:
@@ -446,4 +541,3 @@ const { fetchJson } = useProtectedFetch({
446
541
  ## License
447
542
 
448
543
  MIT � Saeed Aghdam
449
-
package/dist/index.d.mts 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 };