antaeus.keycloak.react 1.0.7 → 2.1.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 +135 -706
- package/dist/index.d.mts +307 -11
- package/dist/index.d.ts +307 -11
- package/dist/index.js +765 -132
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +763 -135
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,812 +1,241 @@
|
|
|
1
1
|
# antaeus.keycloak.react
|
|
2
2
|
|
|
3
|
-
|
|
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)
|
|
3
|
+
Production-ready Keycloak integration for React with zero-config setup
|
|
28
4
|
|
|
29
5
|
## Installation
|
|
30
6
|
|
|
31
7
|
```bash
|
|
32
8
|
npm install antaeus.keycloak.react
|
|
33
|
-
# or
|
|
34
|
-
yarn add antaeus.keycloak.react
|
|
35
|
-
# or
|
|
36
|
-
pnpm add antaeus.keycloak.react
|
|
37
9
|
```
|
|
38
10
|
|
|
39
|
-
## Quick Start
|
|
40
|
-
|
|
41
|
-
### 1. Import styles and wrap your app with KeycloakProvider
|
|
11
|
+
## Quick Start (Zero-Config)
|
|
42
12
|
|
|
43
13
|
```tsx
|
|
44
|
-
// main.tsx
|
|
45
14
|
import { KeycloakProvider } from 'antaeus.keycloak.react';
|
|
46
|
-
import 'antaeus.keycloak.react/styles.css'; // Import CSS
|
|
47
15
|
|
|
48
16
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
49
17
|
<KeycloakProvider
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
clientId: 'my-app',
|
|
53
|
-
}}
|
|
18
|
+
authority="https://sso.example.com/realms/myrealm"
|
|
19
|
+
clientId="my-app"
|
|
54
20
|
>
|
|
55
21
|
<App />
|
|
56
22
|
</KeycloakProvider>
|
|
57
23
|
);
|
|
58
24
|
```
|
|
59
25
|
|
|
60
|
-
|
|
26
|
+
That's it! You now have full authentication with automatic token refresh.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- Zero-config setup
|
|
31
|
+
- Automatic token lifecycle management
|
|
32
|
+
- Token refresh on app startup
|
|
33
|
+
- Silent token renewal
|
|
34
|
+
- Session monitoring UI
|
|
35
|
+
- Protected routes
|
|
36
|
+
- API hooks with auto-auth
|
|
37
|
+
- Device flow support
|
|
38
|
+
- TypeScript support
|
|
39
|
+
|
|
40
|
+
## Usage in Components
|
|
61
41
|
|
|
62
42
|
```tsx
|
|
63
|
-
|
|
64
|
-
import { useAuth, LoginButton, LogoutButton, UserProfile } from 'antaeus.keycloak.react';
|
|
43
|
+
import { useAuth, useProtectedFetch } from 'antaeus.keycloak.react';
|
|
65
44
|
|
|
66
|
-
function
|
|
45
|
+
function MyComponent() {
|
|
67
46
|
const auth = useAuth();
|
|
47
|
+
const { fetchJson } = useProtectedFetch({ baseUrl: 'https://api.example.com' });
|
|
68
48
|
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
}
|
|
49
|
+
const loadData = async () => {
|
|
50
|
+
const data = await fetchJson('/api/data'); // Token added automatically
|
|
51
|
+
console.log(data);
|
|
52
|
+
};
|
|
83
53
|
|
|
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
|
-
);
|
|
54
|
+
return <button onClick={loadData}>Load</button>;
|
|
96
55
|
}
|
|
97
56
|
```
|
|
98
57
|
|
|
99
|
-
|
|
58
|
+
## Configuration Modes
|
|
59
|
+
|
|
60
|
+
### 1. Zero-Config (Recommended)
|
|
100
61
|
|
|
101
62
|
```tsx
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
}
|
|
63
|
+
<KeycloakProvider authority="..." clientId="my-app">
|
|
64
|
+
<App />
|
|
65
|
+
</KeycloakProvider>
|
|
132
66
|
```
|
|
133
67
|
|
|
134
|
-
|
|
68
|
+
### 2. With Preset
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<KeycloakProvider
|
|
72
|
+
authority="..."
|
|
73
|
+
clientId="my-app"
|
|
74
|
+
preset="mobile" // 'default' | 'mobile' | 'high-security' | 'long-running'
|
|
75
|
+
>
|
|
76
|
+
<App />
|
|
77
|
+
</KeycloakProvider>
|
|
78
|
+
```
|
|
135
79
|
|
|
136
|
-
###
|
|
80
|
+
### 3. With Overrides
|
|
137
81
|
|
|
138
82
|
```tsx
|
|
139
83
|
<KeycloakProvider
|
|
84
|
+
authority="..."
|
|
85
|
+
clientId="my-app"
|
|
140
86
|
config={{
|
|
141
|
-
|
|
142
|
-
|
|
87
|
+
silentRenew: {
|
|
88
|
+
maxRefreshTokenLifetime: 604800, // 7 days
|
|
89
|
+
showLoadingIndicator: true,
|
|
90
|
+
}
|
|
143
91
|
}}
|
|
144
92
|
>
|
|
145
93
|
<App />
|
|
146
94
|
</KeycloakProvider>
|
|
147
95
|
```
|
|
148
96
|
|
|
149
|
-
###
|
|
97
|
+
### 4. Full Configuration
|
|
150
98
|
|
|
151
99
|
```tsx
|
|
152
100
|
<KeycloakProvider
|
|
153
101
|
config={{
|
|
154
102
|
authority: 'https://sso.example.com/realms/myrealm',
|
|
155
103
|
clientId: 'my-app',
|
|
156
|
-
redirectUri: window.location.origin,
|
|
157
|
-
postLogoutRedirectUri: window.location.origin,
|
|
158
104
|
scope: 'openid profile email offline_access',
|
|
159
|
-
|
|
160
|
-
// Feature flags
|
|
161
|
-
features: {
|
|
162
|
-
autoRefresh: true,
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
// Silent token renewal
|
|
166
105
|
silentRenew: {
|
|
167
|
-
|
|
168
|
-
|
|
106
|
+
refreshStrategy: 'both',
|
|
107
|
+
maxRefreshTokenLifetime: 2592000,
|
|
169
108
|
},
|
|
170
|
-
|
|
171
|
-
// Event callbacks
|
|
172
109
|
events: {
|
|
173
|
-
onLogin: (user) => console.log('
|
|
174
|
-
|
|
175
|
-
onTokenExpired: () => console.log('Token expired'),
|
|
176
|
-
onError: (error) => console.error('Auth error', error),
|
|
177
|
-
},
|
|
110
|
+
onLogin: (user) => console.log('Logged in', user),
|
|
111
|
+
}
|
|
178
112
|
}}
|
|
179
|
-
debug={true} // Enable debug logging
|
|
180
113
|
>
|
|
181
114
|
<App />
|
|
182
115
|
</KeycloakProvider>
|
|
183
116
|
```
|
|
184
117
|
|
|
185
|
-
##
|
|
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...
|
|
118
|
+
## Configuration Presets
|
|
227
119
|
|
|
228
|
-
|
|
120
|
+
| Preset | Session Lifetime | Best For |
|
|
121
|
+
|--------|------------------|----------|
|
|
122
|
+
| default | 30 days | Most web apps |
|
|
123
|
+
| mobile | 30 days | PWA/Mobile apps |
|
|
124
|
+
| high-security | 24 hours | Banking, healthcare |
|
|
125
|
+
| long-running | 7 days | Desktop apps |
|
|
229
126
|
|
|
230
|
-
|
|
127
|
+
## Token Refresh Strategies
|
|
231
128
|
|
|
232
|
-
**
|
|
233
|
-
-
|
|
234
|
-
-
|
|
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
|
|
129
|
+
- **startup**: Check on app startup
|
|
130
|
+
- **on-demand**: Only when API returns 401
|
|
131
|
+
- **both** (recommended): Startup + 401 retry
|
|
238
132
|
|
|
239
|
-
|
|
133
|
+
## React Hooks
|
|
240
134
|
|
|
241
|
-
|
|
135
|
+
### useAuth()
|
|
242
136
|
|
|
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
137
|
```tsx
|
|
256
|
-
const
|
|
257
|
-
baseUrl: 'http://localhost:5000',
|
|
258
|
-
});
|
|
138
|
+
const auth = useAuth();
|
|
259
139
|
|
|
260
|
-
|
|
140
|
+
auth.isAuthenticated
|
|
141
|
+
auth.isLoading
|
|
142
|
+
auth.user
|
|
143
|
+
auth.signinRedirect()
|
|
144
|
+
auth.signoutRedirect()
|
|
261
145
|
```
|
|
262
146
|
|
|
263
|
-
###
|
|
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.
|
|
147
|
+
### useProtectedFetch()
|
|
288
148
|
|
|
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
149
|
```tsx
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
|
150
|
+
const { fetchJson, isLoading } = useProtectedFetch({
|
|
151
|
+
baseUrl: 'https://api.example.com',
|
|
152
|
+
retryOn401: true,
|
|
153
|
+
});
|
|
335
154
|
|
|
336
|
-
|
|
337
|
-
```tsx
|
|
338
|
-
<DeviceLogin
|
|
339
|
-
apiBaseUrl="http://localhost:5000"
|
|
340
|
-
onSuccess={(user) => navigate('/dashboard')}
|
|
341
|
-
/>
|
|
155
|
+
const data = await fetchJson('/api/data');
|
|
342
156
|
```
|
|
343
157
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
### Role-Based Access
|
|
158
|
+
### useRoles()
|
|
347
159
|
|
|
348
160
|
```tsx
|
|
349
|
-
|
|
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 />}
|
|
161
|
+
const { hasRole, hasAnyRole } = useRoles();
|
|
359
162
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
);
|
|
163
|
+
if (hasRole('admin')) {
|
|
164
|
+
return <AdminPanel />;
|
|
363
165
|
}
|
|
364
166
|
```
|
|
365
167
|
|
|
366
|
-
|
|
168
|
+
## Protected Routes
|
|
367
169
|
|
|
368
170
|
```tsx
|
|
369
|
-
|
|
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
|
|
171
|
+
import { ProtectedRoute } from 'antaeus.keycloak.react';
|
|
421
172
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
}
|
|
173
|
+
<Route
|
|
174
|
+
path="/dashboard"
|
|
175
|
+
element={
|
|
176
|
+
<ProtectedRoute>
|
|
177
|
+
<DashboardPage />
|
|
178
|
+
</ProtectedRoute>
|
|
179
|
+
}
|
|
180
|
+
/>
|
|
444
181
|
```
|
|
445
182
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
183
|
+
## Configuration Reference
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface KeycloakConfig {
|
|
187
|
+
authority: string; // Required
|
|
188
|
+
clientId: string; // Required
|
|
189
|
+
scope?: string; // Default: 'openid profile email offline_access'
|
|
190
|
+
|
|
191
|
+
features?: {
|
|
192
|
+
autoRefresh?: boolean; // Default: true
|
|
193
|
+
sessionMonitor?: boolean; // Default: true
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
silentRenew?: {
|
|
197
|
+
enabled?: boolean; // Default: true
|
|
198
|
+
thresholdSeconds?: number; // Default: 300
|
|
199
|
+
refreshStrategy?: 'startup' | 'on-demand' | 'both';
|
|
200
|
+
maxRefreshTokenLifetime?: number; // Default: 2592000 (30 days)
|
|
201
|
+
showLoadingIndicator?: boolean; // Default: false
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
events?: {
|
|
205
|
+
onLogin?: (user: User) => void;
|
|
206
|
+
onLogout?: () => void;
|
|
207
|
+
onTokenExpired?: () => void;
|
|
208
|
+
onSilentRenewError?: (error: Error) => void;
|
|
209
|
+
};
|
|
469
210
|
}
|
|
470
211
|
```
|
|
471
212
|
|
|
472
|
-
|
|
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
|
|
213
|
+
## What's New in v2.0.0
|
|
500
214
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
-
|
|
504
|
-
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
---
|
|
215
|
+
- Zero-config setup
|
|
216
|
+
- Automatic token refresh on app startup
|
|
217
|
+
- Configuration presets
|
|
218
|
+
- Enhanced token lifecycle management
|
|
219
|
+
- Auto-included SessionMonitor
|
|
220
|
+
- Built-in error notifications
|
|
519
221
|
|
|
520
222
|
## Troubleshooting
|
|
521
223
|
|
|
522
|
-
###
|
|
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
|
|
224
|
+
### 401 errors after app reopens?
|
|
577
225
|
|
|
578
226
|
```tsx
|
|
579
|
-
|
|
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;">
|
|
227
|
+
config={{ silentRenew: { refreshStrategy: 'both' } }}
|
|
597
228
|
```
|
|
598
229
|
|
|
599
|
-
###
|
|
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
|
-
```
|
|
230
|
+
### Silent renew not working?
|
|
606
231
|
|
|
607
|
-
|
|
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
|
|
232
|
+
Make sure offline_access scope is included:
|
|
615
233
|
|
|
616
234
|
```tsx
|
|
617
|
-
|
|
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
|
-
## Architecture
|
|
628
|
-
|
|
629
|
-
### How It Works
|
|
630
|
-
|
|
631
|
-
```
|
|
632
|
-
┌──────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
633
|
-
│ React App │◄───────┤ Keycloak │────────►│ Backend API │
|
|
634
|
-
│ (Your Frontend) │ OIDC │ Auth Server │ OIDC │ (Your Service) │
|
|
635
|
-
└──────────────────┘ └─────────────────┘ └─────────────────┘
|
|
636
|
-
│ │ │
|
|
637
|
-
│ 1. User clicks login │ │
|
|
638
|
-
├──────────────────────────►│ │
|
|
639
|
-
│ │ │
|
|
640
|
-
│ 2. Redirects to Keycloak │ │
|
|
641
|
-
│ login page │ │
|
|
642
|
-
│ │ │
|
|
643
|
-
│ 3. User enters │ │
|
|
644
|
-
│ credentials │ │
|
|
645
|
-
│ │ │
|
|
646
|
-
│ 4. Redirect back with │ │
|
|
647
|
-
│◄──────────────────────────┤ │
|
|
648
|
-
│ auth code │ │
|
|
649
|
-
│ │ │
|
|
650
|
-
│ 5. Exchange code for │ │
|
|
651
|
-
├──────────────────────────►│ │
|
|
652
|
-
│ tokens (PKCE) │ │
|
|
653
|
-
│ │ │
|
|
654
|
-
│ 6. Access + ID + Refresh │ │
|
|
655
|
-
│◄──────────────────────────┤ │
|
|
656
|
-
│ tokens (JWT) │ │
|
|
657
|
-
│ │ │
|
|
658
|
-
│ 7. API call with Bearer token │
|
|
659
|
-
├──────────────────────────────────────────────────────►│
|
|
660
|
-
│ │ │
|
|
661
|
-
│ │ 8. API validates token │
|
|
662
|
-
│ │◄──────────────────────────┤
|
|
663
|
-
│ │ │
|
|
664
|
-
│ 9. Protected data │ │
|
|
665
|
-
│◄──────────────────────────────────────────────────────┤
|
|
235
|
+
config={{ scope: 'openid profile email offline_access' }}
|
|
666
236
|
```
|
|
667
237
|
|
|
668
|
-
### Device Flow (for TVs/IoT)
|
|
669
|
-
|
|
670
|
-
```
|
|
671
|
-
TV/Device App Keycloak User Browser Backend
|
|
672
|
-
│ │ │ │
|
|
673
|
-
│ 1. Request device code │ │ │
|
|
674
|
-
├────────────────────────►│ │ │
|
|
675
|
-
│ │ │ │
|
|
676
|
-
│ 2. Device code + │ │ │
|
|
677
|
-
│ user code + URL │ │ │
|
|
678
|
-
│◄────────────────────────┤ │ │
|
|
679
|
-
│ │ │ │
|
|
680
|
-
│ 3. Display QR code │ │ │
|
|
681
|
-
│ with user code │ │ │
|
|
682
|
-
│ (ABCD-EFGH) │ │ │
|
|
683
|
-
│ │ │ │
|
|
684
|
-
│ │ 4. User scans QR │ │
|
|
685
|
-
│ │ or visits URL │ │
|
|
686
|
-
│ │◄────────────────────┤ │
|
|
687
|
-
│ │ │ │
|
|
688
|
-
│ │ 5. User enters │ │
|
|
689
|
-
│ │ device code │ │
|
|
690
|
-
│ │◄────────────────────┤ │
|
|
691
|
-
│ │ │ │
|
|
692
|
-
│ │ 6. User logs in │ │
|
|
693
|
-
│ │◄────────────────────┤ │
|
|
694
|
-
│ │ │ │
|
|
695
|
-
│ 7. Poll for token │ │ │
|
|
696
|
-
│ (every 5 seconds) │ │ │
|
|
697
|
-
├────────────────────────►│ │ │
|
|
698
|
-
│ │ │ │
|
|
699
|
-
│ 8. Access token │ │ │
|
|
700
|
-
│◄────────────────────────┤ │ │
|
|
701
|
-
│ │ │ │
|
|
702
|
-
│ 9. API call with token │ │ │
|
|
703
|
-
├───────────────────────────────────────────────────────────────────►│
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### Component Hierarchy
|
|
707
|
-
|
|
708
|
-
```
|
|
709
|
-
<KeycloakProvider>
|
|
710
|
-
│
|
|
711
|
-
├─ Manages OIDC client
|
|
712
|
-
├─ Handles token storage
|
|
713
|
-
├─ Provides auth context
|
|
714
|
-
│
|
|
715
|
-
├─ <App>
|
|
716
|
-
│ │
|
|
717
|
-
│ ├─ <SessionMonitor /> (monitors token expiration)
|
|
718
|
-
│ │
|
|
719
|
-
│ ├─ <Router>
|
|
720
|
-
│ │ │
|
|
721
|
-
│ │ ├─ <Route path="/" element={<Home />} />
|
|
722
|
-
│ │ │
|
|
723
|
-
│ │ ├─ <Route path="/dashboard">
|
|
724
|
-
│ │ │ <ProtectedRoute>
|
|
725
|
-
│ │ │ <Dashboard />
|
|
726
|
-
│ │ │ </ProtectedRoute>
|
|
727
|
-
│ │ │ </Route>
|
|
728
|
-
│ │ │
|
|
729
|
-
│ │ └─ <Route path="/admin">
|
|
730
|
-
│ │ <ProtectedRoute requiredRoles={["admin"]}>
|
|
731
|
-
│ │ <AdminPanel />
|
|
732
|
-
│ │ </ProtectedRoute>
|
|
733
|
-
│ │ </Route>
|
|
734
|
-
│ │
|
|
735
|
-
│ └─ <UserProfile /> (displays user info)
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
---
|
|
739
|
-
|
|
740
|
-
## Official Keycloak Documentation
|
|
741
|
-
|
|
742
|
-
Quick links to relevant Keycloak documentation for deeper understanding.
|
|
743
|
-
|
|
744
|
-
### Getting Started
|
|
745
|
-
- [Keycloak Documentation](https://www.keycloak.org/docs/latest/) - Official docs home
|
|
746
|
-
- [Getting Started Guide](https://www.keycloak.org/guides#getting-started) - Install and basics
|
|
747
|
-
- [Server Administration Guide](https://www.keycloak.org/docs/latest/server_admin/) - Configure realms, clients, users
|
|
748
|
-
|
|
749
|
-
### JavaScript/React Integration
|
|
750
|
-
- [Securing Applications - JavaScript](https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter) - Official JS adapter (we use oidc-client-ts instead)
|
|
751
|
-
- [OpenID Connect](https://www.keycloak.org/docs/latest/securing_apps/#_oidc) - OIDC flows explained
|
|
752
|
-
|
|
753
|
-
### Client Configuration
|
|
754
|
-
- [Public Clients](https://www.keycloak.org/docs/latest/server_admin/#_clients) - Configure browser-based apps
|
|
755
|
-
- [Client Scopes](https://www.keycloak.org/docs/latest/server_admin/#_client_scopes) - Manage OAuth scopes
|
|
756
|
-
- [Protocol Mappers](https://www.keycloak.org/docs/latest/server_admin/#_protocol-mappers) - Customize token claims
|
|
757
|
-
- [Valid Redirect URIs](https://www.keycloak.org/docs/latest/server_admin/#_redirect-uris) - Configure allowed redirect URLs
|
|
758
|
-
|
|
759
|
-
### Security Features
|
|
760
|
-
- [PKCE (Proof Key for Code Exchange)](https://www.keycloak.org/docs/latest/securing_apps/#_javascript_implicit_flow) - Enhanced security for SPAs
|
|
761
|
-
- [Token Refresh](https://www.keycloak.org/docs/latest/securing_apps/#_refresh_token) - Silent token renewal
|
|
762
|
-
- [CORS Configuration](https://www.keycloak.org/docs/latest/server_admin/#_cors) - Cross-origin requests
|
|
763
|
-
|
|
764
|
-
### Advanced Topics
|
|
765
|
-
- [Device Authorization Grant](https://www.keycloak.org/docs/latest/securing_apps/#_device_authorization_grant) - OAuth 2.0 device flow for TVs/IoT
|
|
766
|
-
- [Session Management](https://www.keycloak.org/docs/latest/securing_apps/#_session_iframe) - Session timeout and renewal
|
|
767
|
-
- [Logout](https://www.keycloak.org/docs/latest/securing_apps/#_logout) - Single logout configuration
|
|
768
|
-
- [Identity Brokering](https://www.keycloak.org/docs/latest/server_admin/#_identity_broker) - Social login (Google, GitHub, etc.)
|
|
769
|
-
- [User Federation](https://www.keycloak.org/docs/latest/server_admin/#_user-storage-federation) - LDAP, Active Directory
|
|
770
|
-
|
|
771
|
-
### Role-Based Access Control
|
|
772
|
-
- [Realm Roles](https://www.keycloak.org/docs/latest/server_admin/#realm-roles) - Define and assign roles
|
|
773
|
-
- [Client Roles](https://www.keycloak.org/docs/latest/server_admin/#client-roles) - Application-specific roles
|
|
774
|
-
- [Composite Roles](https://www.keycloak.org/docs/latest/server_admin/#composite-roles) - Role hierarchies
|
|
775
|
-
|
|
776
|
-
### REST API & Endpoints
|
|
777
|
-
- [OpenID Connect Endpoints](https://www.keycloak.org/docs/latest/securing_apps/#_oidc_endpoints) - Token, auth, userinfo endpoints
|
|
778
|
-
- [Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/index.html) - Manage Keycloak programmatically
|
|
779
|
-
|
|
780
|
-
### Troubleshooting
|
|
781
|
-
- [Common Issues](https://www.keycloak.org/docs/latest/securing_apps/#_troubleshooting) - Fix common problems
|
|
782
|
-
- [Debugging](https://www.keycloak.org/docs/latest/server_admin/#_auditing) - Enable audit logging
|
|
783
|
-
|
|
784
|
-
---
|
|
785
|
-
|
|
786
|
-
## Requirements
|
|
787
|
-
|
|
788
|
-
- React 18+
|
|
789
|
-
- react-router-dom 6+ (for ProtectedRoute)
|
|
790
|
-
- Modern browser with ES2020 support
|
|
791
|
-
|
|
792
|
-
## Related Documentation
|
|
793
|
-
|
|
794
|
-
- 📖 [Keycloak Setup Guide](../../KEYCLOAK_SETUP_GUIDE.md) - Complete Keycloak configuration guide
|
|
795
|
-
- 📖 [Antaeus.Keycloak.AspNetCore](../keycloak-aspnetcore/README.md) - Backend API library
|
|
796
|
-
- 📖 [Quick Start](../../QUICKSTART.md) - Get started quickly
|
|
797
|
-
- 📖 [Deployment Guide](../../DEPLOYMENT.md) - Production deployment
|
|
798
|
-
|
|
799
238
|
## License
|
|
800
239
|
|
|
801
|
-
MIT
|
|
802
|
-
|
|
803
|
-
## Support
|
|
804
|
-
|
|
805
|
-
For issues and feature requests, please visit [GitHub Issues](https://github.com/sso/keycloak-react/issues).
|
|
806
|
-
|
|
807
|
-
### Additional Resources
|
|
240
|
+
MIT � Saeed Aghdam
|
|
808
241
|
|
|
809
|
-
- [OAuth 2.0 / OpenID Connect Explained](https://www.oauth.com/)
|
|
810
|
-
- [oidc-client-ts Documentation](https://github.com/authts/oidc-client-ts) - Underlying OIDC library
|
|
811
|
-
- [React Router v6 Docs](https://reactrouter.com/) - For routing integration
|
|
812
|
-
- [JWT.io](https://jwt.io/) - Decode and inspect JWT tokens
|