antaeus.keycloak.react 1.0.6
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/LICENSE +21 -0
- package/README.md +652 -0
- package/dist/index.d.mts +1193 -0
- package/dist/index.d.ts +1193 -0
- package/dist/index.js +956 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +937 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +601 -0
- package/package.json +72 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { User, UserManagerSettings } from 'oidc-client-ts';
|
|
3
|
+
export { User } from 'oidc-client-ts';
|
|
4
|
+
import { useLocation } from 'react-router-dom';
|
|
5
|
+
import * as react_oidc_context from 'react-oidc-context';
|
|
6
|
+
export { AuthContextProps as UseAuthReturn } from 'react-oidc-context';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Main configuration interface for Keycloak integration
|
|
10
|
+
*/
|
|
11
|
+
interface KeycloakConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Keycloak realm authority URL (e.g., https://sso.example.com/realms/myrealm)
|
|
14
|
+
*/
|
|
15
|
+
authority: string;
|
|
16
|
+
/**
|
|
17
|
+
* Client ID registered in Keycloak
|
|
18
|
+
*/
|
|
19
|
+
clientId: string;
|
|
20
|
+
/**
|
|
21
|
+
* Redirect URI after login (defaults to current origin)
|
|
22
|
+
*/
|
|
23
|
+
redirectUri?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Redirect URI after logout (defaults to current origin)
|
|
26
|
+
*/
|
|
27
|
+
postLogoutRedirectUri?: string;
|
|
28
|
+
/**
|
|
29
|
+
* OAuth2 scopes to request
|
|
30
|
+
* @default 'openid profile email offline_access'
|
|
31
|
+
*/
|
|
32
|
+
scope?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Feature flags to enable/disable functionality
|
|
35
|
+
*/
|
|
36
|
+
features?: FeatureFlags;
|
|
37
|
+
/**
|
|
38
|
+
* Silent token renewal configuration
|
|
39
|
+
*/
|
|
40
|
+
silentRenew?: SilentRenewConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Event callbacks
|
|
43
|
+
*/
|
|
44
|
+
events?: EventCallbacks;
|
|
45
|
+
/**
|
|
46
|
+
* Advanced OIDC options
|
|
47
|
+
*/
|
|
48
|
+
advanced?: AdvancedOidcOptions;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Feature flags for optional Keycloak functionality
|
|
52
|
+
*/
|
|
53
|
+
interface FeatureFlags {
|
|
54
|
+
/**
|
|
55
|
+
* Enable device flow authentication
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
deviceFlow?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Enable session monitoring UI
|
|
61
|
+
* @default false
|
|
62
|
+
*/
|
|
63
|
+
sessionMonitor?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Enable automatic token refresh
|
|
66
|
+
* @default true
|
|
67
|
+
*/
|
|
68
|
+
autoRefresh?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Silent token renewal configuration
|
|
72
|
+
*/
|
|
73
|
+
interface SilentRenewConfig {
|
|
74
|
+
/**
|
|
75
|
+
* Enable silent token renewal
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Path to silent renew HTML file
|
|
81
|
+
* @default '/silent-renew.html'
|
|
82
|
+
*/
|
|
83
|
+
silentRedirectUri?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Threshold in seconds before expiry to trigger renewal
|
|
86
|
+
* @default 300 (5 minutes)
|
|
87
|
+
*/
|
|
88
|
+
thresholdSeconds?: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Event callbacks for authentication lifecycle
|
|
92
|
+
*/
|
|
93
|
+
interface EventCallbacks {
|
|
94
|
+
/**
|
|
95
|
+
* Called after successful login
|
|
96
|
+
*/
|
|
97
|
+
onLogin?: (user: User) => void;
|
|
98
|
+
/**
|
|
99
|
+
* Called after logout
|
|
100
|
+
*/
|
|
101
|
+
onLogout?: () => void;
|
|
102
|
+
/**
|
|
103
|
+
* Called when token expires
|
|
104
|
+
*/
|
|
105
|
+
onTokenExpired?: () => void;
|
|
106
|
+
/**
|
|
107
|
+
* Called on authentication errors
|
|
108
|
+
*/
|
|
109
|
+
onError?: (error: Error) => void;
|
|
110
|
+
/**
|
|
111
|
+
* Called on silent renew error
|
|
112
|
+
*/
|
|
113
|
+
onSilentRenewError?: (error: Error) => void;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Advanced OIDC configuration options
|
|
117
|
+
*/
|
|
118
|
+
interface AdvancedOidcOptions {
|
|
119
|
+
/**
|
|
120
|
+
* Response type
|
|
121
|
+
* @default 'code'
|
|
122
|
+
*/
|
|
123
|
+
responseType?: string;
|
|
124
|
+
/**
|
|
125
|
+
* PKCE code challenge method
|
|
126
|
+
* @default 'S256'
|
|
127
|
+
*/
|
|
128
|
+
codeChallengeMethod?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Load user info from userinfo endpoint
|
|
131
|
+
* @default true
|
|
132
|
+
*/
|
|
133
|
+
loadUserInfo?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Automatically sign in if session exists
|
|
136
|
+
* @default false
|
|
137
|
+
*/
|
|
138
|
+
automaticSilentSignIn?: boolean;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface KeycloakProviderProps {
|
|
142
|
+
/**
|
|
143
|
+
* Keycloak configuration
|
|
144
|
+
*/
|
|
145
|
+
config: KeycloakConfig;
|
|
146
|
+
/**
|
|
147
|
+
* Child components to render
|
|
148
|
+
*/
|
|
149
|
+
children: React.ReactNode;
|
|
150
|
+
/**
|
|
151
|
+
* Enable debug logging to console
|
|
152
|
+
* @default false
|
|
153
|
+
*/
|
|
154
|
+
debug?: boolean;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Main Keycloak authentication provider component
|
|
158
|
+
*
|
|
159
|
+
* Wraps your application to provide authentication context using Keycloak.
|
|
160
|
+
* This component handles all OIDC flows including login, logout, token refresh,
|
|
161
|
+
* and silent renewal.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* import { KeycloakProvider } from 'antaeus.keycloak.react';
|
|
166
|
+
*
|
|
167
|
+
* function App() {
|
|
168
|
+
* return (
|
|
169
|
+
* <KeycloakProvider
|
|
170
|
+
* config={{
|
|
171
|
+
* authority: 'https://sso.example.com/realms/myrealm',
|
|
172
|
+
* clientId: 'my-app',
|
|
173
|
+
* }}
|
|
174
|
+
* >
|
|
175
|
+
* <YourApp />
|
|
176
|
+
* </KeycloakProvider>
|
|
177
|
+
* );
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare const KeycloakProvider: React.FC<KeycloakProviderProps>;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Context object passed to custom policy function
|
|
185
|
+
*/
|
|
186
|
+
interface PolicyContext {
|
|
187
|
+
/**
|
|
188
|
+
* Current location from react-router
|
|
189
|
+
*/
|
|
190
|
+
location: ReturnType<typeof useLocation>;
|
|
191
|
+
/**
|
|
192
|
+
* Any additional context you want to pass
|
|
193
|
+
*/
|
|
194
|
+
[key: string]: any;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Custom policy function for fine-grained access control
|
|
198
|
+
*/
|
|
199
|
+
type PolicyFunction = (user: User, context: PolicyContext) => boolean;
|
|
200
|
+
interface ProtectedRouteProps {
|
|
201
|
+
/**
|
|
202
|
+
* Child components to render when user is authorized
|
|
203
|
+
*/
|
|
204
|
+
children: React.ReactNode;
|
|
205
|
+
/**
|
|
206
|
+
* Required realm roles (OR logic - user needs at least one)
|
|
207
|
+
*/
|
|
208
|
+
requiredRoles?: string[];
|
|
209
|
+
/**
|
|
210
|
+
* Require all roles instead of any (AND logic)
|
|
211
|
+
* @default false
|
|
212
|
+
*/
|
|
213
|
+
requireAllRoles?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Required OAuth 2.0 scopes (OR logic - user needs at least one)
|
|
216
|
+
*/
|
|
217
|
+
requiredScopes?: string[];
|
|
218
|
+
/**
|
|
219
|
+
* Require all scopes instead of any (AND logic)
|
|
220
|
+
* @default false
|
|
221
|
+
*/
|
|
222
|
+
requireAllScopes?: boolean;
|
|
223
|
+
/**
|
|
224
|
+
* Custom policy function for advanced authorization logic
|
|
225
|
+
* If provided, this takes precedence over role and scope checks
|
|
226
|
+
*/
|
|
227
|
+
policy?: PolicyFunction;
|
|
228
|
+
/**
|
|
229
|
+
* Custom loading component to display while checking authentication
|
|
230
|
+
*/
|
|
231
|
+
loadingComponent?: React.ReactNode;
|
|
232
|
+
/**
|
|
233
|
+
* Custom unauthorized component to display when user lacks required permissions
|
|
234
|
+
*/
|
|
235
|
+
unauthorizedComponent?: React.ReactNode;
|
|
236
|
+
/**
|
|
237
|
+
* Path to redirect to if not authenticated
|
|
238
|
+
* @default '/login'
|
|
239
|
+
*/
|
|
240
|
+
redirectTo?: string;
|
|
241
|
+
/**
|
|
242
|
+
* Callback when user is unauthorized
|
|
243
|
+
* @param reason - Reason for unauthorized access
|
|
244
|
+
*/
|
|
245
|
+
onUnauthorized?: (reason: 'not-authenticated' | 'insufficient-roles' | 'insufficient-scopes' | 'policy-denied') => void;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Component for protecting routes with authentication, role-based, scope-based, or policy-based authorization
|
|
249
|
+
*
|
|
250
|
+
* This component provides comprehensive route protection with multiple authorization strategies:
|
|
251
|
+
* - Authentication check (always required)
|
|
252
|
+
* - Role-based authorization (realm roles)
|
|
253
|
+
* - Scope-based authorization (OAuth 2.0 scopes)
|
|
254
|
+
* - Custom policy functions (advanced logic)
|
|
255
|
+
*
|
|
256
|
+
* @example Basic authentication required
|
|
257
|
+
* ```tsx
|
|
258
|
+
* import { ProtectedRoute } from 'antaeus.keycloak.react';
|
|
259
|
+
*
|
|
260
|
+
* <Route
|
|
261
|
+
* path="/dashboard"
|
|
262
|
+
* element={
|
|
263
|
+
* <ProtectedRoute>
|
|
264
|
+
* <Dashboard />
|
|
265
|
+
* </ProtectedRoute>
|
|
266
|
+
* }
|
|
267
|
+
* />
|
|
268
|
+
* ```
|
|
269
|
+
*
|
|
270
|
+
* @example Require specific role
|
|
271
|
+
* ```tsx
|
|
272
|
+
* <Route
|
|
273
|
+
* path="/admin"
|
|
274
|
+
* element={
|
|
275
|
+
* <ProtectedRoute requiredRoles={['admin']}>
|
|
276
|
+
* <AdminPanel />
|
|
277
|
+
* </ProtectedRoute>
|
|
278
|
+
* }
|
|
279
|
+
* />
|
|
280
|
+
* ```
|
|
281
|
+
*
|
|
282
|
+
* @example Require any of multiple roles
|
|
283
|
+
* ```tsx
|
|
284
|
+
* <ProtectedRoute requiredRoles={['editor', 'moderator', 'admin']}>
|
|
285
|
+
* <EditPage />
|
|
286
|
+
* </ProtectedRoute>
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @example Require all roles (AND logic)
|
|
290
|
+
* ```tsx
|
|
291
|
+
* <ProtectedRoute
|
|
292
|
+
* requiredRoles={['verified', 'premium']}
|
|
293
|
+
* requireAllRoles={true}
|
|
294
|
+
* >
|
|
295
|
+
* <PremiumContent />
|
|
296
|
+
* </ProtectedRoute>
|
|
297
|
+
* ```
|
|
298
|
+
*
|
|
299
|
+
* @example Require specific scopes
|
|
300
|
+
* ```tsx
|
|
301
|
+
* <ProtectedRoute requiredScopes={['read:users', 'write:users']}>
|
|
302
|
+
* <UserManagement />
|
|
303
|
+
* </ProtectedRoute>
|
|
304
|
+
* ```
|
|
305
|
+
*
|
|
306
|
+
* @example Require all scopes (AND logic)
|
|
307
|
+
* ```tsx
|
|
308
|
+
* <ProtectedRoute
|
|
309
|
+
* requiredScopes={['read:data', 'write:data', 'delete:data']}
|
|
310
|
+
* requireAllScopes={true}
|
|
311
|
+
* >
|
|
312
|
+
* <DataAdmin />
|
|
313
|
+
* </ProtectedRoute>
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* @example Combine roles and scopes
|
|
317
|
+
* ```tsx
|
|
318
|
+
* <ProtectedRoute
|
|
319
|
+
* requiredRoles={['admin']}
|
|
320
|
+
* requiredScopes={['manage:users']}
|
|
321
|
+
* >
|
|
322
|
+
* <UserAdmin />
|
|
323
|
+
* </ProtectedRoute>
|
|
324
|
+
* ```
|
|
325
|
+
*
|
|
326
|
+
* @example Custom policy function
|
|
327
|
+
* ```tsx
|
|
328
|
+
* <ProtectedRoute
|
|
329
|
+
* policy={(user, context) => {
|
|
330
|
+
* // Custom authorization logic
|
|
331
|
+
* const isEmailVerified = user.profile.email_verified === true;
|
|
332
|
+
* const isAdult = (user.profile.age || 0) >= 18;
|
|
333
|
+
* return isEmailVerified && isAdult;
|
|
334
|
+
* }}
|
|
335
|
+
* onUnauthorized={(reason) => {
|
|
336
|
+
* console.log('Access denied:', reason);
|
|
337
|
+
* }}
|
|
338
|
+
* >
|
|
339
|
+
* <AdultContent />
|
|
340
|
+
* </ProtectedRoute>
|
|
341
|
+
* ```
|
|
342
|
+
*
|
|
343
|
+
* @example Advanced policy with location context
|
|
344
|
+
* ```tsx
|
|
345
|
+
* <ProtectedRoute
|
|
346
|
+
* policy={(user, context) => {
|
|
347
|
+
* // Check if user is accessing during business hours
|
|
348
|
+
* const hour = new Date().getHours();
|
|
349
|
+
* const isBusinessHours = hour >= 9 && hour < 17;
|
|
350
|
+
*
|
|
351
|
+
* // Or check based on route
|
|
352
|
+
* const isAdminRoute = context.location.pathname.startsWith('/admin');
|
|
353
|
+
* const hasAdminRole = user.profile.realm_access?.roles?.includes('admin');
|
|
354
|
+
*
|
|
355
|
+
* return isBusinessHours && (!isAdminRoute || hasAdminRole);
|
|
356
|
+
* }}
|
|
357
|
+
* >
|
|
358
|
+
* <TimeSensitiveContent />
|
|
359
|
+
* </ProtectedRoute>
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
declare const ProtectedRoute: React.FC<ProtectedRouteProps>;
|
|
363
|
+
|
|
364
|
+
interface LoginButtonProps {
|
|
365
|
+
/**
|
|
366
|
+
* Button text
|
|
367
|
+
* @default 'Login'
|
|
368
|
+
*/
|
|
369
|
+
children?: React.ReactNode;
|
|
370
|
+
/**
|
|
371
|
+
* Additional CSS class names
|
|
372
|
+
*/
|
|
373
|
+
className?: string;
|
|
374
|
+
/**
|
|
375
|
+
* Button variant
|
|
376
|
+
* @default 'primary'
|
|
377
|
+
*/
|
|
378
|
+
variant?: 'primary' | 'secondary' | 'outline';
|
|
379
|
+
/**
|
|
380
|
+
* Button size
|
|
381
|
+
* @default 'medium'
|
|
382
|
+
*/
|
|
383
|
+
size?: 'small' | 'medium' | 'large';
|
|
384
|
+
/**
|
|
385
|
+
* Show login icon
|
|
386
|
+
* @default true
|
|
387
|
+
*/
|
|
388
|
+
showIcon?: boolean;
|
|
389
|
+
/**
|
|
390
|
+
* Callback before login starts
|
|
391
|
+
*/
|
|
392
|
+
onLoginStart?: () => void;
|
|
393
|
+
/**
|
|
394
|
+
* Custom inline styles
|
|
395
|
+
*/
|
|
396
|
+
style?: React.CSSProperties;
|
|
397
|
+
/**
|
|
398
|
+
* Disable the button
|
|
399
|
+
*/
|
|
400
|
+
disabled?: boolean;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Pre-built login button component
|
|
404
|
+
*
|
|
405
|
+
* Triggers the Keycloak login flow when clicked. The button automatically
|
|
406
|
+
* hides when the user is already authenticated.
|
|
407
|
+
*
|
|
408
|
+
* @example Basic usage
|
|
409
|
+
* ```tsx
|
|
410
|
+
* import { LoginButton } from 'antaeus.keycloak.react';
|
|
411
|
+
*
|
|
412
|
+
* function Header() {
|
|
413
|
+
* return <LoginButton />;
|
|
414
|
+
* }
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* @example Customized
|
|
418
|
+
* ```tsx
|
|
419
|
+
* <LoginButton
|
|
420
|
+
* variant="outline"
|
|
421
|
+
* size="large"
|
|
422
|
+
* onLoginStart={() => console.log('Starting login...')}
|
|
423
|
+
* >
|
|
424
|
+
* Sign In
|
|
425
|
+
* </LoginButton>
|
|
426
|
+
* ```
|
|
427
|
+
*
|
|
428
|
+
* @example With custom styling
|
|
429
|
+
* ```tsx
|
|
430
|
+
* <LoginButton
|
|
431
|
+
* className="my-custom-class"
|
|
432
|
+
* style={{ backgroundColor: '#007bff' }}
|
|
433
|
+
* />
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
declare const LoginButton: React.FC<LoginButtonProps>;
|
|
437
|
+
|
|
438
|
+
interface LogoutButtonProps {
|
|
439
|
+
/**
|
|
440
|
+
* Button text
|
|
441
|
+
* @default 'Logout'
|
|
442
|
+
*/
|
|
443
|
+
children?: React.ReactNode;
|
|
444
|
+
/**
|
|
445
|
+
* Additional CSS class names
|
|
446
|
+
*/
|
|
447
|
+
className?: string;
|
|
448
|
+
/**
|
|
449
|
+
* Button variant
|
|
450
|
+
* @default 'secondary'
|
|
451
|
+
*/
|
|
452
|
+
variant?: 'primary' | 'secondary' | 'outline';
|
|
453
|
+
/**
|
|
454
|
+
* Button size
|
|
455
|
+
* @default 'medium'
|
|
456
|
+
*/
|
|
457
|
+
size?: 'small' | 'medium' | 'large';
|
|
458
|
+
/**
|
|
459
|
+
* Show logout icon
|
|
460
|
+
* @default true
|
|
461
|
+
*/
|
|
462
|
+
showIcon?: boolean;
|
|
463
|
+
/**
|
|
464
|
+
* Callback before logout starts
|
|
465
|
+
*/
|
|
466
|
+
onLogoutStart?: () => void;
|
|
467
|
+
/**
|
|
468
|
+
* Custom inline styles
|
|
469
|
+
*/
|
|
470
|
+
style?: React.CSSProperties;
|
|
471
|
+
/**
|
|
472
|
+
* Disable the button
|
|
473
|
+
*/
|
|
474
|
+
disabled?: boolean;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Pre-built logout button component
|
|
478
|
+
*
|
|
479
|
+
* Triggers the Keycloak logout flow when clicked. The button automatically
|
|
480
|
+
* hides when the user is not authenticated.
|
|
481
|
+
*
|
|
482
|
+
* @example Basic usage
|
|
483
|
+
* ```tsx
|
|
484
|
+
* import { LogoutButton } from 'antaeus.keycloak.react';
|
|
485
|
+
*
|
|
486
|
+
* function Header() {
|
|
487
|
+
* return <LogoutButton />;
|
|
488
|
+
* }
|
|
489
|
+
* ```
|
|
490
|
+
*
|
|
491
|
+
* @example Customized
|
|
492
|
+
* ```tsx
|
|
493
|
+
* <LogoutButton
|
|
494
|
+
* variant="outline"
|
|
495
|
+
* size="small"
|
|
496
|
+
* onLogoutStart={() => {
|
|
497
|
+
* // Clean up app state
|
|
498
|
+
* localStorage.clear();
|
|
499
|
+
* }}
|
|
500
|
+
* >
|
|
501
|
+
* Sign Out
|
|
502
|
+
* </LogoutButton>
|
|
503
|
+
* ```
|
|
504
|
+
*
|
|
505
|
+
* @example With custom styling
|
|
506
|
+
* ```tsx
|
|
507
|
+
* <LogoutButton
|
|
508
|
+
* className="my-logout-btn"
|
|
509
|
+
* style={{ backgroundColor: '#dc3545' }}
|
|
510
|
+
* />
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
declare const LogoutButton: React.FC<LogoutButtonProps>;
|
|
514
|
+
|
|
515
|
+
interface UserProfileProps {
|
|
516
|
+
/**
|
|
517
|
+
* Additional CSS class names
|
|
518
|
+
*/
|
|
519
|
+
className?: string;
|
|
520
|
+
/**
|
|
521
|
+
* Show user avatar/icon
|
|
522
|
+
* @default true
|
|
523
|
+
*/
|
|
524
|
+
showAvatar?: boolean;
|
|
525
|
+
/**
|
|
526
|
+
* Show user email
|
|
527
|
+
* @default true
|
|
528
|
+
*/
|
|
529
|
+
showEmail?: boolean;
|
|
530
|
+
/**
|
|
531
|
+
* Show user roles
|
|
532
|
+
* @default false
|
|
533
|
+
*/
|
|
534
|
+
showRoles?: boolean;
|
|
535
|
+
/**
|
|
536
|
+
* Avatar size in pixels
|
|
537
|
+
* @default 40
|
|
538
|
+
*/
|
|
539
|
+
avatarSize?: number;
|
|
540
|
+
/**
|
|
541
|
+
* Custom inline styles
|
|
542
|
+
*/
|
|
543
|
+
style?: React.CSSProperties;
|
|
544
|
+
/**
|
|
545
|
+
* Callback when profile is clicked
|
|
546
|
+
*/
|
|
547
|
+
onClick?: () => void;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Pre-built user profile display component
|
|
551
|
+
*
|
|
552
|
+
* Displays the current user's information including name, email, and optionally roles.
|
|
553
|
+
* Automatically hides when user is not authenticated.
|
|
554
|
+
*
|
|
555
|
+
* @example Basic usage
|
|
556
|
+
* ```tsx
|
|
557
|
+
* import { UserProfile } from 'antaeus.keycloak.react';
|
|
558
|
+
*
|
|
559
|
+
* function Header() {
|
|
560
|
+
* return <UserProfile />;
|
|
561
|
+
* }
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* @example With roles
|
|
565
|
+
* ```tsx
|
|
566
|
+
* <UserProfile showRoles={true} />
|
|
567
|
+
* ```
|
|
568
|
+
*
|
|
569
|
+
* @example Clickable profile
|
|
570
|
+
* ```tsx
|
|
571
|
+
* <UserProfile
|
|
572
|
+
* onClick={() => navigate('/profile')}
|
|
573
|
+
* style={{ cursor: 'pointer' }}
|
|
574
|
+
* />
|
|
575
|
+
* ```
|
|
576
|
+
*
|
|
577
|
+
* @example Customized
|
|
578
|
+
* ```tsx
|
|
579
|
+
* <UserProfile
|
|
580
|
+
* showEmail={false}
|
|
581
|
+
* showRoles={true}
|
|
582
|
+
* avatarSize={50}
|
|
583
|
+
* className="my-profile"
|
|
584
|
+
* />
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
declare const UserProfile: React.FC<UserProfileProps>;
|
|
588
|
+
|
|
589
|
+
interface SessionMonitorProps {
|
|
590
|
+
/**
|
|
591
|
+
* Position on screen
|
|
592
|
+
* @default 'top-right'
|
|
593
|
+
*/
|
|
594
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
595
|
+
/**
|
|
596
|
+
* Warning threshold in seconds (yellow state)
|
|
597
|
+
* @default 300 (5 minutes)
|
|
598
|
+
*/
|
|
599
|
+
warningThreshold?: number;
|
|
600
|
+
/**
|
|
601
|
+
* Critical threshold in seconds (red state)
|
|
602
|
+
* @default 60 (1 minute)
|
|
603
|
+
*/
|
|
604
|
+
criticalThreshold?: number;
|
|
605
|
+
/**
|
|
606
|
+
* Show progress bar
|
|
607
|
+
* @default true
|
|
608
|
+
*/
|
|
609
|
+
showProgressBar?: boolean;
|
|
610
|
+
/**
|
|
611
|
+
* Allow minimize/expand
|
|
612
|
+
* @default true
|
|
613
|
+
*/
|
|
614
|
+
minimizable?: boolean;
|
|
615
|
+
/**
|
|
616
|
+
* Start minimized
|
|
617
|
+
* @default false
|
|
618
|
+
*/
|
|
619
|
+
startMinimized?: boolean;
|
|
620
|
+
/**
|
|
621
|
+
* Custom CSS class names
|
|
622
|
+
*/
|
|
623
|
+
className?: string;
|
|
624
|
+
/**
|
|
625
|
+
* Callback when warning threshold is reached
|
|
626
|
+
*/
|
|
627
|
+
onWarning?: (secondsRemaining: number) => void;
|
|
628
|
+
/**
|
|
629
|
+
* Callback when critical threshold is reached
|
|
630
|
+
*/
|
|
631
|
+
onCritical?: (secondsRemaining: number) => void;
|
|
632
|
+
/**
|
|
633
|
+
* Callback when session expires
|
|
634
|
+
*/
|
|
635
|
+
onExpired?: () => void;
|
|
636
|
+
/**
|
|
637
|
+
* Custom inline styles
|
|
638
|
+
*/
|
|
639
|
+
style?: React.CSSProperties;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Session monitoring component with countdown timer
|
|
643
|
+
*
|
|
644
|
+
* Displays the time remaining until the user's session expires. Shows visual
|
|
645
|
+
* warnings when the session is about to expire and triggers callbacks for
|
|
646
|
+
* custom handling.
|
|
647
|
+
*
|
|
648
|
+
* @example Basic usage
|
|
649
|
+
* ```tsx
|
|
650
|
+
* import { SessionMonitor } from 'antaeus.keycloak.react';
|
|
651
|
+
*
|
|
652
|
+
* function App() {
|
|
653
|
+
* return (
|
|
654
|
+
* <>
|
|
655
|
+
* <SessionMonitor />
|
|
656
|
+
* <YourApp />
|
|
657
|
+
* </>
|
|
658
|
+
* );
|
|
659
|
+
* }
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @example With custom thresholds
|
|
663
|
+
* ```tsx
|
|
664
|
+
* <SessionMonitor
|
|
665
|
+
* warningThreshold={600} // 10 minutes
|
|
666
|
+
* criticalThreshold={120} // 2 minutes
|
|
667
|
+
* onWarning={(seconds) => {
|
|
668
|
+
* console.log(`Session expiring in ${seconds} seconds`);
|
|
669
|
+
* }}
|
|
670
|
+
* />
|
|
671
|
+
* ```
|
|
672
|
+
*
|
|
673
|
+
* @example Custom position and style
|
|
674
|
+
* ```tsx
|
|
675
|
+
* <SessionMonitor
|
|
676
|
+
* position="bottom-left"
|
|
677
|
+
* showProgressBar={false}
|
|
678
|
+
* minimizable={false}
|
|
679
|
+
* />
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
declare const SessionMonitor: React.FC<SessionMonitorProps>;
|
|
683
|
+
|
|
684
|
+
interface DeviceLoginProps {
|
|
685
|
+
/**
|
|
686
|
+
* API base URL for device flow endpoints
|
|
687
|
+
* @example 'http://localhost:5000'
|
|
688
|
+
*/
|
|
689
|
+
apiBaseUrl: string;
|
|
690
|
+
/**
|
|
691
|
+
* QR code size in pixels
|
|
692
|
+
* @default 256
|
|
693
|
+
*/
|
|
694
|
+
qrCodeSize?: number;
|
|
695
|
+
/**
|
|
696
|
+
* Polling interval in seconds
|
|
697
|
+
* @default 5
|
|
698
|
+
*/
|
|
699
|
+
pollingInterval?: number;
|
|
700
|
+
/**
|
|
701
|
+
* Custom CSS class
|
|
702
|
+
*/
|
|
703
|
+
className?: string;
|
|
704
|
+
/**
|
|
705
|
+
* Callback on successful login
|
|
706
|
+
*/
|
|
707
|
+
onSuccess?: (user: User) => void;
|
|
708
|
+
/**
|
|
709
|
+
* Callback on error
|
|
710
|
+
*/
|
|
711
|
+
onError?: (error: Error) => void;
|
|
712
|
+
/**
|
|
713
|
+
* Custom inline styles
|
|
714
|
+
*/
|
|
715
|
+
style?: React.CSSProperties;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Device flow login component
|
|
719
|
+
*
|
|
720
|
+
* Displays QR code and user code for device authentication. Automatically polls
|
|
721
|
+
* for authorization and completes the login flow.
|
|
722
|
+
*
|
|
723
|
+
* @example Basic usage
|
|
724
|
+
* ```tsx
|
|
725
|
+
* import { DeviceLogin } from 'antaeus.keycloak.react';
|
|
726
|
+
*
|
|
727
|
+
* function TVLoginPage() {
|
|
728
|
+
* return (
|
|
729
|
+
* <DeviceLogin
|
|
730
|
+
* apiBaseUrl="http://localhost:5000"
|
|
731
|
+
* onSuccess={(user) => {
|
|
732
|
+
* console.log('Logged in:', user.profile.name);
|
|
733
|
+
* navigate('/home');
|
|
734
|
+
* }}
|
|
735
|
+
* />
|
|
736
|
+
* );
|
|
737
|
+
* }
|
|
738
|
+
* ```
|
|
739
|
+
*
|
|
740
|
+
* @example With custom settings
|
|
741
|
+
* ```tsx
|
|
742
|
+
* <DeviceLogin
|
|
743
|
+
* apiBaseUrl="http://localhost:5000"
|
|
744
|
+
* qrCodeSize={300}
|
|
745
|
+
* pollingInterval={3}
|
|
746
|
+
* onError={(error) => console.error('Login failed:', error)}
|
|
747
|
+
* />
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
declare const DeviceLogin: React.FC<DeviceLoginProps>;
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Main authentication hook for accessing Keycloak authentication state and methods
|
|
754
|
+
*
|
|
755
|
+
* This hook provides access to the current user, authentication state, and methods
|
|
756
|
+
* for signing in and out. It must be used within a KeycloakProvider component.
|
|
757
|
+
*
|
|
758
|
+
* @returns Authentication state and methods
|
|
759
|
+
*
|
|
760
|
+
* @example
|
|
761
|
+
* ```tsx
|
|
762
|
+
* import { useAuth } from 'antaeus.keycloak.react';
|
|
763
|
+
*
|
|
764
|
+
* function MyComponent() {
|
|
765
|
+
* const auth = useAuth();
|
|
766
|
+
*
|
|
767
|
+
* if (auth.isLoading) {
|
|
768
|
+
* return <div>Loading...</div>;
|
|
769
|
+
* }
|
|
770
|
+
*
|
|
771
|
+
* if (!auth.isAuthenticated) {
|
|
772
|
+
* return <button onClick={() => auth.signinRedirect()}>Login</button>;
|
|
773
|
+
* }
|
|
774
|
+
*
|
|
775
|
+
* return (
|
|
776
|
+
* <div>
|
|
777
|
+
* <p>Welcome, {auth.user?.profile?.name}</p>
|
|
778
|
+
* <button onClick={() => auth.signoutRedirect()}>Logout</button>
|
|
779
|
+
* </div>
|
|
780
|
+
* );
|
|
781
|
+
* }
|
|
782
|
+
* ```
|
|
783
|
+
*
|
|
784
|
+
* @example Role checking
|
|
785
|
+
* ```tsx
|
|
786
|
+
* function AdminPanel() {
|
|
787
|
+
* const auth = useAuth();
|
|
788
|
+
* const roles = auth.user?.profile?.realm_access?.roles || [];
|
|
789
|
+
*
|
|
790
|
+
* if (!roles.includes('admin')) {
|
|
791
|
+
* return <div>Access denied</div>;
|
|
792
|
+
* }
|
|
793
|
+
*
|
|
794
|
+
* return <div>Admin panel content</div>;
|
|
795
|
+
* }
|
|
796
|
+
* ```
|
|
797
|
+
*/
|
|
798
|
+
declare const useAuth: () => react_oidc_context.AuthContextProps;
|
|
799
|
+
|
|
800
|
+
interface UseRolesReturn {
|
|
801
|
+
/**
|
|
802
|
+
* Array of realm roles assigned to the current user
|
|
803
|
+
*/
|
|
804
|
+
roles: string[];
|
|
805
|
+
/**
|
|
806
|
+
* Checks if user has a specific realm role
|
|
807
|
+
* @param role - Role name to check
|
|
808
|
+
* @returns True if user has the role
|
|
809
|
+
*/
|
|
810
|
+
hasRole: (role: string) => boolean;
|
|
811
|
+
/**
|
|
812
|
+
* Checks if user has any of the specified roles (OR logic)
|
|
813
|
+
* @param roles - Array of role names
|
|
814
|
+
* @returns True if user has at least one of the roles
|
|
815
|
+
*/
|
|
816
|
+
hasAnyRole: (roles: string[]) => boolean;
|
|
817
|
+
/**
|
|
818
|
+
* Checks if user has all of the specified roles (AND logic)
|
|
819
|
+
* @param roles - Array of role names
|
|
820
|
+
* @returns True if user has all the roles
|
|
821
|
+
*/
|
|
822
|
+
hasAllRoles: (roles: string[]) => boolean;
|
|
823
|
+
/**
|
|
824
|
+
* Gets client-specific roles
|
|
825
|
+
* @param clientId - Client ID to get roles for
|
|
826
|
+
* @returns Array of client roles
|
|
827
|
+
*/
|
|
828
|
+
getClientRoles: (clientId: string) => string[];
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Hook for working with Keycloak roles
|
|
832
|
+
*
|
|
833
|
+
* Provides convenient methods for checking user roles, both realm-level and client-level.
|
|
834
|
+
* Must be used within a KeycloakProvider component.
|
|
835
|
+
*
|
|
836
|
+
* @returns Role utilities and methods
|
|
837
|
+
*
|
|
838
|
+
* @example Basic usage
|
|
839
|
+
* ```tsx
|
|
840
|
+
* import { useRoles } from 'antaeus.keycloak.react';
|
|
841
|
+
*
|
|
842
|
+
* function MyComponent() {
|
|
843
|
+
* const { roles, hasRole, hasAnyRole } = useRoles();
|
|
844
|
+
*
|
|
845
|
+
* return (
|
|
846
|
+
* <div>
|
|
847
|
+
* <p>Your roles: {roles.join(', ')}</p>
|
|
848
|
+
* {hasRole('admin') && <AdminPanel />}
|
|
849
|
+
* {hasAnyRole(['editor', 'moderator']) && <EditButton />}
|
|
850
|
+
* </div>
|
|
851
|
+
* );
|
|
852
|
+
* }
|
|
853
|
+
* ```
|
|
854
|
+
*
|
|
855
|
+
* @example Client roles
|
|
856
|
+
* ```tsx
|
|
857
|
+
* function ClientRoleExample() {
|
|
858
|
+
* const { getClientRoles } = useRoles();
|
|
859
|
+
* const apiRoles = getClientRoles('my-api');
|
|
860
|
+
*
|
|
861
|
+
* return <div>API roles: {apiRoles.join(', ')}</div>;
|
|
862
|
+
* }
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
declare function useRoles(): UseRolesReturn;
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* Return type for useScopes hook
|
|
869
|
+
*/
|
|
870
|
+
interface UseScopesReturn {
|
|
871
|
+
/**
|
|
872
|
+
* Array of granted scopes
|
|
873
|
+
*/
|
|
874
|
+
scopes: string[];
|
|
875
|
+
/**
|
|
876
|
+
* Check if user has a specific scope
|
|
877
|
+
*/
|
|
878
|
+
hasScope: (scope: string) => boolean;
|
|
879
|
+
/**
|
|
880
|
+
* Check if user has any of the specified scopes
|
|
881
|
+
*/
|
|
882
|
+
hasAnyScope: (scopes: string[]) => boolean;
|
|
883
|
+
/**
|
|
884
|
+
* Check if user has all of the specified scopes
|
|
885
|
+
*/
|
|
886
|
+
hasAllScopes: (scopes: string[]) => boolean;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Hook for checking OAuth 2.0 scopes
|
|
890
|
+
*
|
|
891
|
+
* Provides utilities for scope-based authorization. Scopes are typically space-separated
|
|
892
|
+
* in the token (e.g., "openid profile email read:users write:users").
|
|
893
|
+
*
|
|
894
|
+
* @returns Scope checking utilities
|
|
895
|
+
*
|
|
896
|
+
* @example Basic usage
|
|
897
|
+
* ```tsx
|
|
898
|
+
* import { useScopes } from 'antaeus.keycloak.react';
|
|
899
|
+
*
|
|
900
|
+
* function MyComponent() {
|
|
901
|
+
* const { scopes, hasScope, hasAnyScope } = useScopes();
|
|
902
|
+
*
|
|
903
|
+
* return (
|
|
904
|
+
* <div>
|
|
905
|
+
* <p>Your scopes: {scopes.join(', ')}</p>
|
|
906
|
+
*
|
|
907
|
+
* {hasScope('read:users') && <UserList />}
|
|
908
|
+
*
|
|
909
|
+
* {hasAnyScope(['write:users', 'admin']) && <CreateUserButton />}
|
|
910
|
+
* </div>
|
|
911
|
+
* );
|
|
912
|
+
* }
|
|
913
|
+
* ```
|
|
914
|
+
*
|
|
915
|
+
* @example Conditional rendering
|
|
916
|
+
* ```tsx
|
|
917
|
+
* function DataActions() {
|
|
918
|
+
* const { hasScope, hasAllScopes } = useScopes();
|
|
919
|
+
*
|
|
920
|
+
* return (
|
|
921
|
+
* <div>
|
|
922
|
+
* {hasScope('read:data') && <ViewButton />}
|
|
923
|
+
* {hasScope('write:data') && <EditButton />}
|
|
924
|
+
* {hasScope('delete:data') && <DeleteButton />}
|
|
925
|
+
* {hasAllScopes(['read:data', 'write:data']) && <ExportButton />}
|
|
926
|
+
* </div>
|
|
927
|
+
* );
|
|
928
|
+
* }
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
declare function useScopes(): UseScopesReturn;
|
|
932
|
+
|
|
933
|
+
interface UseProtectedFetchOptions {
|
|
934
|
+
/**
|
|
935
|
+
* Base URL for API requests
|
|
936
|
+
* @example 'http://localhost:5000'
|
|
937
|
+
*/
|
|
938
|
+
baseUrl?: string;
|
|
939
|
+
/**
|
|
940
|
+
* Callback when request is unauthorized (401)
|
|
941
|
+
*/
|
|
942
|
+
onUnauthorized?: () => void;
|
|
943
|
+
/**
|
|
944
|
+
* Retry request on 401 error after token refresh
|
|
945
|
+
* @default true
|
|
946
|
+
*/
|
|
947
|
+
retryOn401?: boolean;
|
|
948
|
+
/**
|
|
949
|
+
* Additional headers to include in all requests
|
|
950
|
+
*/
|
|
951
|
+
headers?: Record<string, string>;
|
|
952
|
+
}
|
|
953
|
+
interface UseProtectedFetchReturn {
|
|
954
|
+
/**
|
|
955
|
+
* Fetch with automatic token injection and JSON parsing
|
|
956
|
+
*
|
|
957
|
+
* @param url - URL or path to fetch
|
|
958
|
+
* @param init - Fetch init options
|
|
959
|
+
* @returns Parsed JSON response
|
|
960
|
+
*/
|
|
961
|
+
fetchJson: <T = any>(url: string, init?: RequestInit) => Promise<T>;
|
|
962
|
+
/**
|
|
963
|
+
* Raw fetch with automatic token injection
|
|
964
|
+
*
|
|
965
|
+
* @param url - URL or path to fetch
|
|
966
|
+
* @param init - Fetch init options
|
|
967
|
+
* @returns Fetch response
|
|
968
|
+
*/
|
|
969
|
+
fetch: (url: string, init?: RequestInit) => Promise<Response>;
|
|
970
|
+
/**
|
|
971
|
+
* Check if request is in progress
|
|
972
|
+
*/
|
|
973
|
+
isLoading: boolean;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Hook for making authenticated API requests
|
|
977
|
+
*
|
|
978
|
+
* Automatically injects the current user's access token into all requests.
|
|
979
|
+
* Handles 401 errors and provides convenient methods for JSON APIs.
|
|
980
|
+
*
|
|
981
|
+
* @param options - Configuration options
|
|
982
|
+
* @returns Protected fetch methods
|
|
983
|
+
*
|
|
984
|
+
* @example Basic usage
|
|
985
|
+
* ```tsx
|
|
986
|
+
* import { useProtectedFetch } from 'antaeus.keycloak.react';
|
|
987
|
+
*
|
|
988
|
+
* function MyComponent() {
|
|
989
|
+
* const { fetchJson } = useProtectedFetch({
|
|
990
|
+
* baseUrl: 'http://localhost:5000'
|
|
991
|
+
* });
|
|
992
|
+
*
|
|
993
|
+
* const loadData = async () => {
|
|
994
|
+
* // Token is automatically added
|
|
995
|
+
* const data = await fetchJson('/api/data');
|
|
996
|
+
* console.log(data);
|
|
997
|
+
* };
|
|
998
|
+
*
|
|
999
|
+
* return <button onClick={loadData}>Load Data</button>;
|
|
1000
|
+
* }
|
|
1001
|
+
* ```
|
|
1002
|
+
*
|
|
1003
|
+
* @example With custom headers
|
|
1004
|
+
* ```tsx
|
|
1005
|
+
* const { fetchJson } = useProtectedFetch({
|
|
1006
|
+
* baseUrl: 'http://localhost:5000',
|
|
1007
|
+
* headers: {
|
|
1008
|
+
* 'X-Custom-Header': 'value'
|
|
1009
|
+
* }
|
|
1010
|
+
* });
|
|
1011
|
+
* ```
|
|
1012
|
+
*
|
|
1013
|
+
* @example POST request
|
|
1014
|
+
* ```tsx
|
|
1015
|
+
* const { fetchJson } = useProtectedFetch({
|
|
1016
|
+
* baseUrl: 'http://localhost:5000'
|
|
1017
|
+
* });
|
|
1018
|
+
*
|
|
1019
|
+
* const createUser = async (userData: User) => {
|
|
1020
|
+
* const result = await fetchJson<User>('/api/users', {
|
|
1021
|
+
* method: 'POST',
|
|
1022
|
+
* body: JSON.stringify(userData),
|
|
1023
|
+
* headers: {
|
|
1024
|
+
* 'Content-Type': 'application/json'
|
|
1025
|
+
* }
|
|
1026
|
+
* });
|
|
1027
|
+
* return result;
|
|
1028
|
+
* };
|
|
1029
|
+
* ```
|
|
1030
|
+
*
|
|
1031
|
+
* @example Handle unauthorized
|
|
1032
|
+
* ```tsx
|
|
1033
|
+
* const { fetchJson } = useProtectedFetch({
|
|
1034
|
+
* baseUrl: 'http://localhost:5000',
|
|
1035
|
+
* onUnauthorized: () => {
|
|
1036
|
+
* console.log('Unauthorized! Redirecting to login...');
|
|
1037
|
+
* }
|
|
1038
|
+
* });
|
|
1039
|
+
* ```
|
|
1040
|
+
*/
|
|
1041
|
+
declare function useProtectedFetch(options?: UseProtectedFetchOptions): UseProtectedFetchReturn;
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Default configuration values for Keycloak integration
|
|
1045
|
+
*/
|
|
1046
|
+
declare const defaultConfig: Partial<KeycloakConfig>;
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Configuration builder for Keycloak integration
|
|
1050
|
+
*/
|
|
1051
|
+
declare class KeycloakConfigBuilder {
|
|
1052
|
+
/**
|
|
1053
|
+
* Builds OIDC client configuration from Keycloak config
|
|
1054
|
+
*
|
|
1055
|
+
* @param userConfig - User-provided Keycloak configuration
|
|
1056
|
+
* @returns OIDC client settings ready for react-oidc-context
|
|
1057
|
+
* @throws Error if required fields are missing or invalid
|
|
1058
|
+
*/
|
|
1059
|
+
static build(userConfig: KeycloakConfig): UserManagerSettings;
|
|
1060
|
+
/**
|
|
1061
|
+
* Validates required configuration fields
|
|
1062
|
+
*
|
|
1063
|
+
* @param config - Configuration to validate
|
|
1064
|
+
* @throws Error if validation fails
|
|
1065
|
+
*/
|
|
1066
|
+
private static validate;
|
|
1067
|
+
/**
|
|
1068
|
+
* Merges user configuration with defaults
|
|
1069
|
+
*
|
|
1070
|
+
* @param config - User configuration
|
|
1071
|
+
* @returns Merged configuration with all defaults applied
|
|
1072
|
+
*/
|
|
1073
|
+
private static mergeWithDefaults;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Helper function to get configuration from environment variables
|
|
1077
|
+
*
|
|
1078
|
+
* @returns Partial Keycloak configuration from environment
|
|
1079
|
+
*/
|
|
1080
|
+
declare function getConfigFromEnv(): Partial<KeycloakConfig>;
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Device authorization response from Keycloak
|
|
1084
|
+
*/
|
|
1085
|
+
interface DeviceAuthorizationResponse {
|
|
1086
|
+
/** Device code used for polling */
|
|
1087
|
+
device_code: string;
|
|
1088
|
+
/** User code to display to user */
|
|
1089
|
+
user_code: string;
|
|
1090
|
+
/** Verification URI (plain) */
|
|
1091
|
+
verification_uri: string;
|
|
1092
|
+
/** Complete verification URI with user code (for QR codes) */
|
|
1093
|
+
verification_uri_complete: string;
|
|
1094
|
+
/** Expiration time in seconds */
|
|
1095
|
+
expires_in: number;
|
|
1096
|
+
/** Recommended polling interval in seconds */
|
|
1097
|
+
interval: number;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Token response from Keycloak
|
|
1101
|
+
*/
|
|
1102
|
+
interface TokenResponse {
|
|
1103
|
+
/** Access token */
|
|
1104
|
+
access_token: string;
|
|
1105
|
+
/** Token type (usually 'Bearer') */
|
|
1106
|
+
token_type: string;
|
|
1107
|
+
/** Expiration time in seconds */
|
|
1108
|
+
expires_in: number;
|
|
1109
|
+
/** Refresh token (if offline_access scope was requested) */
|
|
1110
|
+
refresh_token?: string;
|
|
1111
|
+
/** ID token */
|
|
1112
|
+
id_token?: string;
|
|
1113
|
+
/** Granted scopes */
|
|
1114
|
+
scope?: string;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Error response from token polling
|
|
1118
|
+
*/
|
|
1119
|
+
interface DeviceFlowError {
|
|
1120
|
+
/** Error code */
|
|
1121
|
+
error: string;
|
|
1122
|
+
/** Error description */
|
|
1123
|
+
error_description?: string;
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Request device authorization code from the backend API
|
|
1127
|
+
*
|
|
1128
|
+
* This function calls your backend API which proxies the request to Keycloak.
|
|
1129
|
+
* This avoids CORS issues.
|
|
1130
|
+
*
|
|
1131
|
+
* @param apiBaseUrl - Base URL of your backend API (e.g., 'http://localhost:5000')
|
|
1132
|
+
* @returns Device authorization response
|
|
1133
|
+
* @throws Error if request fails
|
|
1134
|
+
*
|
|
1135
|
+
* @example
|
|
1136
|
+
* ```ts
|
|
1137
|
+
* const response = await requestDeviceCode('http://localhost:5000');
|
|
1138
|
+
* console.log('User code:', response.user_code);
|
|
1139
|
+
* console.log('Verification URL:', response.verification_uri_complete);
|
|
1140
|
+
* ```
|
|
1141
|
+
*/
|
|
1142
|
+
declare function requestDeviceCode(apiBaseUrl: string): Promise<DeviceAuthorizationResponse>;
|
|
1143
|
+
/**
|
|
1144
|
+
* Poll for token using device code
|
|
1145
|
+
*
|
|
1146
|
+
* This function should be called repeatedly until the user authorizes the device
|
|
1147
|
+
* or the device code expires. The backend handles the actual token exchange with Keycloak.
|
|
1148
|
+
*
|
|
1149
|
+
* @param apiBaseUrl - Base URL of your backend API
|
|
1150
|
+
* @param deviceCode - Device code from requestDeviceCode()
|
|
1151
|
+
* @returns Token response if authorized, null if still pending
|
|
1152
|
+
* @throws Error if request fails or authorization is denied
|
|
1153
|
+
*
|
|
1154
|
+
* @example
|
|
1155
|
+
* ```ts
|
|
1156
|
+
* const deviceCode = 'ABC123';
|
|
1157
|
+
* const interval = setInterval(async () => {
|
|
1158
|
+
* try {
|
|
1159
|
+
* const token = await pollForToken('http://localhost:5000', deviceCode);
|
|
1160
|
+
* if (token) {
|
|
1161
|
+
* clearInterval(interval);
|
|
1162
|
+
* console.log('Authorized! Token:', token.access_token);
|
|
1163
|
+
* }
|
|
1164
|
+
* } catch (error) {
|
|
1165
|
+
* if (error.message.includes('authorization_pending')) {
|
|
1166
|
+
* // Still waiting, continue polling
|
|
1167
|
+
* } else {
|
|
1168
|
+
* // Real error, stop polling
|
|
1169
|
+
* clearInterval(interval);
|
|
1170
|
+
* console.error('Authorization failed:', error);
|
|
1171
|
+
* }
|
|
1172
|
+
* }
|
|
1173
|
+
* }, 5000);
|
|
1174
|
+
* ```
|
|
1175
|
+
*/
|
|
1176
|
+
declare function pollForToken(apiBaseUrl: string, deviceCode: string): Promise<TokenResponse | null>;
|
|
1177
|
+
/**
|
|
1178
|
+
* Fetch user information using access token
|
|
1179
|
+
*
|
|
1180
|
+
* @param apiBaseUrl - Base URL of your backend API
|
|
1181
|
+
* @param accessToken - Access token from token response
|
|
1182
|
+
* @returns User information
|
|
1183
|
+
* @throws Error if request fails
|
|
1184
|
+
*
|
|
1185
|
+
* @example
|
|
1186
|
+
* ```ts
|
|
1187
|
+
* const userInfo = await fetchUserInfo('http://localhost:5000', token.access_token);
|
|
1188
|
+
* console.log('User:', userInfo.preferred_username);
|
|
1189
|
+
* ```
|
|
1190
|
+
*/
|
|
1191
|
+
declare function fetchUserInfo(apiBaseUrl: string, accessToken: string): Promise<any>;
|
|
1192
|
+
|
|
1193
|
+
export { type AdvancedOidcOptions, type DeviceAuthorizationResponse, type DeviceFlowError, DeviceLogin, type DeviceLoginProps, type EventCallbacks, type FeatureFlags, type KeycloakConfig, KeycloakConfigBuilder, KeycloakProvider, type KeycloakProviderProps, LoginButton, type LoginButtonProps, LogoutButton, type LogoutButtonProps, type PolicyContext, type PolicyFunction, ProtectedRoute, type ProtectedRouteProps, SessionMonitor, type SessionMonitorProps, type SilentRenewConfig, type TokenResponse, type UseProtectedFetchOptions, type UseProtectedFetchReturn, type UseRolesReturn, type UseScopesReturn, UserProfile, type UserProfileProps, defaultConfig, fetchUserInfo, getConfigFromEnv, pollForToken, requestDeviceCode, useAuth, useProtectedFetch, useRoles, useScopes };
|