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/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
|
+
[](https://www.npmjs.com/package/antaeus.keycloak.react)
|
|
6
|
+
[](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)
|