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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Your Organization
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,652 @@
1
+ # antaeus.keycloak.react
2
+
3
+ > Complete Keycloak integration for React with minimal configuration
4
+
5
+ [![npm version](https://img.shields.io/npm/v/antaeus.keycloak.react.svg)](https://www.npmjs.com/package/antaeus.keycloak.react)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ ### Authentication & Authorization
11
+ - ✅ **Authorization Code + PKCE** - Secure authentication flow
12
+ - ✅ **Device Flow Support** - For TVs, IoT devices, and CLIs
13
+ - ✅ **Protected Routes** - Easy route protection with role checking
14
+ - ✅ **Automatic Token Refresh** - Silent token renewal
15
+ - ✅ **Role-Based Access Control** - Built-in role checking utilities
16
+
17
+ ### UI Components
18
+ - ✅ **Pre-built Components** - LoginButton, LogoutButton, UserProfile
19
+ - ✅ **Session Monitor** - Real-time session countdown with warnings
20
+ - ✅ **Device Login UI** - QR code display with automatic polling
21
+ - ✅ **Professional Styling** - Production-ready CSS with dark mode
22
+
23
+ ### Developer Experience
24
+ - ✅ **TypeScript Support** - Full type safety
25
+ - ✅ **Minimal Configuration** - Works with sensible defaults
26
+ - ✅ **Protected Fetch Hook** - API calls with automatic token injection
27
+ - ✅ **Tiny Bundle** - Only 28 KB (minified, before gzip)
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install antaeus.keycloak.react
33
+ # or
34
+ yarn add antaeus.keycloak.react
35
+ # or
36
+ pnpm add antaeus.keycloak.react
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### 1. Import styles and wrap your app with KeycloakProvider
42
+
43
+ ```tsx
44
+ // main.tsx
45
+ import { KeycloakProvider } from 'antaeus.keycloak.react';
46
+ import 'antaeus.keycloak.react/styles.css'; // Import CSS
47
+
48
+ ReactDOM.createRoot(document.getElementById('root')).render(
49
+ <KeycloakProvider
50
+ config={{
51
+ authority: 'https://sso.example.com/realms/myrealm',
52
+ clientId: 'my-app',
53
+ }}
54
+ >
55
+ <App />
56
+ </KeycloakProvider>
57
+ );
58
+ ```
59
+
60
+ ### 2. Use pre-built UI components
61
+
62
+ ```tsx
63
+ // App.tsx
64
+ import { useAuth, LoginButton, LogoutButton, UserProfile } from 'antaeus.keycloak.react';
65
+
66
+ function App() {
67
+ const auth = useAuth();
68
+
69
+ if (auth.isLoading) {
70
+ return <div>Loading...</div>;
71
+ }
72
+
73
+ if (!auth.isAuthenticated) {
74
+ return (
75
+ <div>
76
+ <h1>Welcome to My App</h1>
77
+ <LoginButton variant="primary" size="large">
78
+ Get Started
79
+ </LoginButton>
80
+ </div>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <div>
86
+ <header>
87
+ <UserProfile showAvatar showEmail showRoles />
88
+ <LogoutButton />
89
+ </header>
90
+ <main>
91
+ <h1>Welcome, {auth.user?.profile?.name}</h1>
92
+ <p>You are logged in!</p>
93
+ </main>
94
+ </div>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ### 3. Protect routes
100
+
101
+ ```tsx
102
+ import { ProtectedRoute } from 'antaeus.keycloak.react';
103
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
104
+
105
+ function App() {
106
+ return (
107
+ <BrowserRouter>
108
+ <Routes>
109
+ <Route path="/" element={<HomePage />} />
110
+
111
+ <Route
112
+ path="/dashboard"
113
+ element={
114
+ <ProtectedRoute>
115
+ <DashboardPage />
116
+ </ProtectedRoute>
117
+ }
118
+ />
119
+
120
+ <Route
121
+ path="/admin"
122
+ element={
123
+ <ProtectedRoute requiredRoles={['admin']}>
124
+ <AdminPage />
125
+ </ProtectedRoute>
126
+ }
127
+ />
128
+ </Routes>
129
+ </BrowserRouter>
130
+ );
131
+ }
132
+ ```
133
+
134
+ ## Configuration
135
+
136
+ ### Basic Configuration
137
+
138
+ ```tsx
139
+ <KeycloakProvider
140
+ config={{
141
+ authority: 'https://sso.example.com/realms/myrealm',
142
+ clientId: 'my-app',
143
+ }}
144
+ >
145
+ <App />
146
+ </KeycloakProvider>
147
+ ```
148
+
149
+ ### Advanced Configuration
150
+
151
+ ```tsx
152
+ <KeycloakProvider
153
+ config={{
154
+ authority: 'https://sso.example.com/realms/myrealm',
155
+ clientId: 'my-app',
156
+ redirectUri: window.location.origin,
157
+ postLogoutRedirectUri: window.location.origin,
158
+ scope: 'openid profile email offline_access',
159
+
160
+ // Feature flags
161
+ features: {
162
+ autoRefresh: true,
163
+ },
164
+
165
+ // Silent token renewal
166
+ silentRenew: {
167
+ enabled: true,
168
+ thresholdSeconds: 300, // Renew 5 minutes before expiry
169
+ },
170
+
171
+ // Event callbacks
172
+ events: {
173
+ onLogin: (user) => console.log('User logged in', user),
174
+ onLogout: () => console.log('User logged out'),
175
+ onTokenExpired: () => console.log('Token expired'),
176
+ onError: (error) => console.error('Auth error', error),
177
+ },
178
+ }}
179
+ debug={true} // Enable debug logging
180
+ >
181
+ <App />
182
+ </KeycloakProvider>
183
+ ```
184
+
185
+ ## API Reference
186
+
187
+ ### Components
188
+
189
+ #### `KeycloakProvider`
190
+
191
+ Main provider component that wraps your application.
192
+
193
+ **Props:**
194
+ - `config` (required): Keycloak configuration
195
+ - `children` (required): Child components
196
+ - `loadingComponent` (optional): Custom loading component
197
+ - `errorComponent` (optional): Custom error component
198
+ - `debug` (optional): Enable debug logging
199
+
200
+ #### `ProtectedRoute`
201
+
202
+ Component for protecting routes that require authentication and/or specific roles.
203
+
204
+ **Props:**
205
+ - `children` (required): Components to render when authorized
206
+ - `requiredRoles` (optional): Array of required roles
207
+ - `requireAllRoles` (optional): Require all roles (AND logic) vs any role (OR logic)
208
+ - `loadingComponent` (optional): Custom loading component
209
+ - `unauthorizedComponent` (optional): Custom unauthorized component
210
+ - `redirectTo` (optional): Redirect path when not authenticated (default: '/login')
211
+ - `onUnauthorized` (optional): Callback when access is denied
212
+
213
+ ### Hooks
214
+
215
+ #### `useAuth()`
216
+
217
+ Main authentication hook.
218
+
219
+ **Returns:**
220
+ - `user`: Current user object
221
+ - `isAuthenticated`: Whether user is authenticated
222
+ - `isLoading`: Whether authentication is in progress
223
+ - `error`: Authentication error if any
224
+ - `signinRedirect()`: Trigger login redirect
225
+ - `signoutRedirect()`: Trigger logout redirect
226
+ - And more...
227
+
228
+ #### `useRoles()`
229
+
230
+ Role checking utilities.
231
+
232
+ **Returns:**
233
+ - `roles`: Array of user's realm roles
234
+ - `hasRole(role)`: Check if user has a specific role
235
+ - `hasAnyRole(roles)`: Check if user has any of the roles
236
+ - `hasAllRoles(roles)`: Check if user has all the roles
237
+ - `getClientRoles(clientId)`: Get client-specific roles
238
+
239
+ #### `useProtectedFetch()`
240
+
241
+ Hook for making authenticated API requests with automatic token injection.
242
+
243
+ **Options:**
244
+ - `baseUrl` (optional): Base URL for API requests
245
+ - `onUnauthorized` (optional): Callback on 401 errors
246
+ - `retryOn401` (optional): Retry after token refresh (default: true)
247
+ - `headers` (optional): Additional headers for all requests
248
+
249
+ **Returns:**
250
+ - `fetchJson<T>()`: Fetch and parse JSON with type safety
251
+ - `fetch()`: Raw fetch with token injection
252
+ - `isLoading`: Whether authentication is loading
253
+
254
+ **Example:**
255
+ ```tsx
256
+ const { fetchJson } = useProtectedFetch({
257
+ baseUrl: 'http://localhost:5000',
258
+ });
259
+
260
+ const data = await fetchJson<User[]>('/api/users');
261
+ ```
262
+
263
+ ### UI Components
264
+
265
+ #### `LoginButton`
266
+
267
+ Pre-built login button with multiple variants.
268
+
269
+ **Props:**
270
+ - `variant`: 'primary' | 'secondary' | 'outline' | 'danger' (default: 'primary')
271
+ - `size`: 'small' | 'medium' | 'large' (default: 'medium')
272
+ - `showIcon`: boolean (default: true)
273
+ - `onLoginStart`: Callback before login
274
+ - `disabled`: boolean
275
+ - `className`: Custom CSS class
276
+ - `style`: Inline styles
277
+
278
+ #### `LogoutButton`
279
+
280
+ Pre-built logout button.
281
+
282
+ **Props:**
283
+ - Same as LoginButton
284
+
285
+ #### `UserProfile`
286
+
287
+ Displays user information with avatar, name, email, and roles.
288
+
289
+ **Props:**
290
+ - `showAvatar`: boolean (default: true)
291
+ - `showEmail`: boolean (default: true)
292
+ - `showRoles`: boolean (default: false)
293
+ - `avatarSize`: number (default: 40)
294
+ - `maxRoles`: number (default: 3)
295
+ - `className`: Custom CSS class
296
+ - `style`: Inline styles
297
+
298
+ #### `SessionMonitor`
299
+
300
+ Real-time session countdown timer with visual warnings.
301
+
302
+ **Props:**
303
+ - `position`: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' (default: 'top-right')
304
+ - `warningThreshold`: Seconds for yellow warning (default: 300)
305
+ - `criticalThreshold`: Seconds for red warning (default: 60)
306
+ - `showProgressBar`: boolean (default: true)
307
+ - `minimizable`: boolean (default: true)
308
+ - `startMinimized`: boolean (default: false)
309
+ - `onWarning`: Callback at warning threshold
310
+ - `onCritical`: Callback at critical threshold
311
+ - `onExpired`: Callback when session expires
312
+
313
+ **Example:**
314
+ ```tsx
315
+ <SessionMonitor
316
+ position="top-right"
317
+ warningThreshold={300}
318
+ criticalThreshold={60}
319
+ onCritical={(seconds) => alert(`Session expiring in ${seconds} seconds!`)}
320
+ />
321
+ ```
322
+
323
+ #### `DeviceLogin`
324
+
325
+ Device flow login component with QR code display.
326
+
327
+ **Props:**
328
+ - `apiBaseUrl`: Backend API URL (required)
329
+ - `qrCodeSize`: QR code size in pixels (default: 256)
330
+ - `pollingInterval`: Polling interval in seconds (default: 5)
331
+ - `onSuccess`: Callback on successful login
332
+ - `onError`: Callback on error
333
+ - `className`: Custom CSS class
334
+ - `style`: Inline styles
335
+
336
+ **Example:**
337
+ ```tsx
338
+ <DeviceLogin
339
+ apiBaseUrl="http://localhost:5000"
340
+ onSuccess={(user) => navigate('/dashboard')}
341
+ />
342
+ ```
343
+
344
+ ## Examples
345
+
346
+ ### Role-Based Access
347
+
348
+ ```tsx
349
+ import { useRoles } from 'antaeus.keycloak.react';
350
+
351
+ function MyComponent() {
352
+ const { roles, hasRole, hasAnyRole } = useRoles();
353
+
354
+ return (
355
+ <div>
356
+ <p>Your roles: {roles.join(', ')}</p>
357
+
358
+ {hasRole('admin') && <AdminPanel />}
359
+
360
+ {hasAnyRole(['editor', 'moderator']) && <EditButton />}
361
+ </div>
362
+ );
363
+ }
364
+ ```
365
+
366
+ ### Multiple Role Requirements
367
+
368
+ ```tsx
369
+ // User needs at least one of these roles
370
+ <ProtectedRoute requiredRoles={['editor', 'moderator', 'admin']}>
371
+ <EditPage />
372
+ </ProtectedRoute>
373
+
374
+ // User needs ALL of these roles
375
+ <ProtectedRoute requiredRoles={['verified', 'premium']} requireAllRoles={true}>
376
+ <PremiumContent />
377
+ </ProtectedRoute>
378
+ ```
379
+
380
+ ### Custom Loading/Error States
381
+
382
+ ```tsx
383
+ <ProtectedRoute
384
+ loadingComponent={<Spinner />}
385
+ unauthorizedComponent={<AccessDenied />}
386
+ >
387
+ <ProtectedContent />
388
+ </ProtectedRoute>
389
+ ```
390
+
391
+ ### Authenticated API Calls
392
+
393
+ ```tsx
394
+ import { useProtectedFetch } from 'antaeus.keycloak.react';
395
+
396
+ function UsersList() {
397
+ const { fetchJson, isLoading } = useProtectedFetch({
398
+ baseUrl: 'http://localhost:5000',
399
+ });
400
+ const [users, setUsers] = useState([]);
401
+
402
+ useEffect(() => {
403
+ fetchJson<User[]>('/api/users')
404
+ .then(setUsers)
405
+ .catch(console.error);
406
+ }, []);
407
+
408
+ if (isLoading) return <div>Loading...</div>;
409
+
410
+ return (
411
+ <ul>
412
+ {users.map(user => (
413
+ <li key={user.id}>{user.name}</li>
414
+ ))}
415
+ </ul>
416
+ );
417
+ }
418
+ ```
419
+
420
+ ### Session Monitoring
421
+
422
+ ```tsx
423
+ import { SessionMonitor } from 'antaeus.keycloak.react';
424
+
425
+ function App() {
426
+ return (
427
+ <>
428
+ <YourApp />
429
+
430
+ {/* Monitor session and warn before expiration */}
431
+ <SessionMonitor
432
+ position="top-right"
433
+ warningThreshold={600} // 10 minutes
434
+ criticalThreshold={120} // 2 minutes
435
+ onWarning={() => console.log('Session expiring soon')}
436
+ onCritical={() => {
437
+ // Show modal or notification
438
+ alert('Your session is about to expire!');
439
+ }}
440
+ />
441
+ </>
442
+ );
443
+ }
444
+ ```
445
+
446
+ ### Device Flow for TVs/IoT
447
+
448
+ ```tsx
449
+ import { DeviceLogin } from 'antaeus.keycloak.react';
450
+ import { useNavigate } from 'react-router-dom';
451
+
452
+ function TVLoginPage() {
453
+ const navigate = useNavigate();
454
+
455
+ return (
456
+ <DeviceLogin
457
+ apiBaseUrl="http://localhost:5000"
458
+ qrCodeSize={300}
459
+ pollingInterval={3}
460
+ onSuccess={(user) => {
461
+ console.log('User logged in:', user.profile.name);
462
+ navigate('/dashboard');
463
+ }}
464
+ onError={(error) => {
465
+ console.error('Login failed:', error);
466
+ }}
467
+ />
468
+ );
469
+ }
470
+ ```
471
+
472
+ ### Environment Variables
473
+
474
+ ```env
475
+ # .env
476
+ VITE_KEYCLOAK_AUTHORITY=https://sso.example.com/realms/myrealm
477
+ VITE_KEYCLOAK_CLIENT_ID=my-app
478
+ ```
479
+
480
+ ```tsx
481
+ import { getConfigFromEnv } from 'antaeus.keycloak.react';
482
+
483
+ const config = {
484
+ ...getConfigFromEnv(),
485
+ // Override or add more config
486
+ };
487
+
488
+ <KeycloakProvider config={config}>
489
+ <App />
490
+ </KeycloakProvider>
491
+ ```
492
+
493
+ ## Keycloak Server Configuration
494
+
495
+ This library requires proper Keycloak server configuration. See the comprehensive guide:
496
+
497
+ 📖 **[Keycloak Setup Guide](../../KEYCLOAK_SETUP_GUIDE.md)**
498
+
499
+ ### Quick Keycloak Checklist
500
+
501
+ Before using this library, ensure your Keycloak server has:
502
+
503
+ - [ ] **Realm created** with appropriate settings
504
+ - [ ] **Public client created** for your React app with:
505
+ - Client authentication: `OFF` (public client)
506
+ - Standard flow: `ON`
507
+ - PKCE enabled: `S256`
508
+ - Valid redirect URIs configured
509
+ - Web origins configured
510
+ - [ ] **Protocol mappers configured**:
511
+ - Realm roles mapper (`realm_access.roles`)
512
+ - Audience mapper (pointing to your API)
513
+ - [ ] **Users and roles created**
514
+ - [ ] **CORS enabled** for your frontend origin
515
+
516
+ See the full guide for detailed step-by-step instructions.
517
+
518
+ ---
519
+
520
+ ## Troubleshooting
521
+
522
+ ### "Invalid redirect_uri" Error
523
+
524
+ **Cause:** The redirect URL doesn't match Keycloak's allowed URIs.
525
+
526
+ **Solution:**
527
+ 1. Check client configuration in Keycloak
528
+ 2. Ensure `redirectUri` in your config matches exactly
529
+ 3. Add `http://localhost:5173/*` to Valid Redirect URIs in Keycloak
530
+
531
+ ### Roles Not Showing Up
532
+
533
+ **Cause:** Missing protocol mapper in Keycloak.
534
+
535
+ **Solution:**
536
+ 1. Add "realm roles mapper" to your client
537
+ 2. Configure: Token Claim Name = `realm_access.roles`
538
+ 3. Enable: Add to access token, ID token
539
+
540
+ ### CORS Errors
541
+
542
+ **Cause:** Keycloak not allowing requests from your origin.
543
+
544
+ **Solution:**
545
+ 1. In Keycloak client settings → Web Origins
546
+ 2. Add `http://localhost:5173` (or your origin)
547
+ 3. Or use `*` for development only
548
+
549
+ ### Token Expired Immediately
550
+
551
+ **Cause:** Clock skew or short token lifespan.
552
+
553
+ **Solution:**
554
+ 1. Check Keycloak Realm Settings → Tokens
555
+ 2. Increase "Access Token Lifespan" (default: 5 min)
556
+ 3. Enable automatic token refresh:
557
+ ```tsx
558
+ features: { autoRefresh: true }
559
+ ```
560
+
561
+ ### Device Flow Not Working
562
+
563
+ **Cause:** Device flow not enabled on Keycloak client.
564
+
565
+ **Solution:**
566
+ 1. In client capabilities, enable: "OAuth 2.0 Device Authorization Grant"
567
+ 2. Set device code lifespan and polling interval
568
+ 3. Ensure backend API has device flow endpoints configured
569
+
570
+ For more troubleshooting, see [Keycloak Setup Guide - Troubleshooting](../../KEYCLOAK_SETUP_GUIDE.md#troubleshooting).
571
+
572
+ ---
573
+
574
+ ## Security Best Practices
575
+
576
+ ### 1. Always Use HTTPS in Production
577
+
578
+ ```tsx
579
+ <KeycloakProvider
580
+ config={{
581
+ authority: 'https://keycloak.example.com/realms/myapp', // ← HTTPS!
582
+ // Never use http:// in production
583
+ }}
584
+ >
585
+ ```
586
+
587
+ ### 2. Don't Store Tokens in localStorage (We Handle This)
588
+
589
+ The library uses `oidc-client-ts` which stores tokens securely in sessionStorage by default. Never manually store tokens in localStorage as it's vulnerable to XSS attacks.
590
+
591
+ ### 3. Use Content Security Policy
592
+
593
+ Add to your `index.html`:
594
+ ```html
595
+ <meta http-equiv="Content-Security-Policy"
596
+ content="default-src 'self'; connect-src 'self' https://your-keycloak.com;">
597
+ ```
598
+
599
+ ### 4. Validate Tokens on the Backend
600
+
601
+ Frontend authentication is for UX only. Always validate tokens on your backend API:
602
+ ```csharp
603
+ // In your .NET API
604
+ builder.Services.AddKeycloakAuthentication(config);
605
+ ```
606
+
607
+ ### 5. Use Short-Lived Access Tokens
608
+
609
+ Configure in Keycloak:
610
+ - Access Token Lifespan: 5 minutes
611
+ - Refresh Token: 30 days
612
+ - Enable automatic refresh in React
613
+
614
+ ### 6. Implement Logout Everywhere
615
+
616
+ ```tsx
617
+ const { signoutRedirect } = useAuth();
618
+
619
+ // This logs out from Keycloak AND your app
620
+ await signoutRedirect();
621
+ ```
622
+
623
+ For complete security guidelines, see [Keycloak Setup Guide - Security](../../KEYCLOAK_SETUP_GUIDE.md#security-best-practices).
624
+
625
+ ---
626
+
627
+ ## Requirements
628
+
629
+ - React 18+
630
+ - react-router-dom 6+ (for ProtectedRoute)
631
+ - Modern browser with ES2020 support
632
+
633
+ ## Related Documentation
634
+
635
+ - 📖 [Keycloak Setup Guide](../../KEYCLOAK_SETUP_GUIDE.md) - Complete Keycloak configuration guide
636
+ - 📖 [Antaeus.Keycloak.AspNetCore](../keycloak-aspnetcore/README.md) - Backend API library
637
+ - 📖 [Quick Start](../../QUICKSTART.md) - Get started quickly
638
+ - 📖 [Deployment Guide](../../DEPLOYMENT.md) - Production deployment
639
+
640
+ ## License
641
+
642
+ MIT
643
+
644
+ ## Support
645
+
646
+ For issues and feature requests, please visit [GitHub Issues](https://github.com/sso/keycloak-react/issues).
647
+
648
+ ### Useful Resources
649
+
650
+ - [Keycloak Official Documentation](https://www.keycloak.org/docs/latest/)
651
+ - [OAuth 2.0 / OpenID Connect Explained](https://www.oauth.com/)
652
+ - [oidc-client-ts Documentation](https://github.com/authts/oidc-client-ts)