keycloak-api-manager 5.0.1 → 5.0.2

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.
@@ -0,0 +1,447 @@
1
+ # Configuration & Authentication
2
+
3
+ Core API methods for initializing and managing the Keycloak Admin Client connection.
4
+
5
+ ## Table of Contents
6
+
7
+ - [configure()](#configure)
8
+ - [setConfig()](#setconfig)
9
+ - [getToken()](#gettoken)
10
+ - [auth()](#auth)
11
+ - [stop()](#stop)
12
+
13
+ ---
14
+
15
+ ## configure()
16
+
17
+ Initialize and authenticate the Keycloak Admin Client. Must be called before using any handler methods.
18
+
19
+ **Syntax:**
20
+ ```javascript
21
+ await KeycloakManager.configure(credentials)
22
+ ```
23
+
24
+ ### Parameters
25
+
26
+ #### credentials (Object) ⚠️ Required
27
+
28
+ Authentication configuration object.
29
+
30
+ | Property | Type | Required | Description |
31
+ |----------|------|----------|-------------|
32
+ | `baseUrl` | string | ⚠️ Yes | Keycloak server base URL (e.g., `https://keycloak.example.com:8443`) |
33
+ | `realmName` | string | ⚠️ Yes | Target realm name for authentication (usually `master`) |
34
+ | `grantType` | string | ⚠️ Yes | OAuth2 grant type: `'password'` or `'client_credentials'` |
35
+ | `clientId` | string | ⚠️ Yes | Client ID (e.g., `admin-cli` for password grant) |
36
+ | `username` | string | 📋 Conditional | Required for `password` grant type |
37
+ | `password` | string | 📋 Conditional | Required for `password` grant type |
38
+ | `clientSecret` | string | 📋 Conditional | Required for `client_credentials` grant type |
39
+ | `tokenLifeSpan` | number | 📋 Optional | Token refresh interval in seconds (default: 60) |
40
+ | `scope` | string | 📋 Optional | OAuth2 scope (e.g., `'offline_access'` for refresh tokens) |
41
+
42
+ ### Returns
43
+
44
+ **Promise\<void\>** - Resolves when authentication succeeds
45
+
46
+ ### Examples
47
+
48
+ #### Password Grant (Admin User)
49
+
50
+ ```javascript
51
+ const KeycloakManager = require('keycloak-api-manager');
52
+
53
+ await KeycloakManager.configure({
54
+ baseUrl: 'https://keycloak.example.com:8443',
55
+ realmName: 'master',
56
+ username: 'admin',
57
+ password: 'admin-password',
58
+ grantType: 'password',
59
+ clientId: 'admin-cli',
60
+ tokenLifeSpan: 60
61
+ });
62
+
63
+ console.log('Authenticated successfully');
64
+ ```
65
+
66
+ #### Client Credentials (Service Account)
67
+
68
+ ```javascript
69
+ await KeycloakManager.configure({
70
+ baseUrl: 'https://keycloak.example.com:8443',
71
+ realmName: 'master',
72
+ clientId: 'my-service-account',
73
+ clientSecret: 'your-client-secret-here',
74
+ grantType: 'client_credentials',
75
+ tokenLifeSpan: 300
76
+ });
77
+ ```
78
+
79
+ #### With Refresh Token Support
80
+
81
+ ```javascript
82
+ await KeycloakManager.configure({
83
+ baseUrl: 'https://keycloak.example.com:8443',
84
+ realmName: 'master',
85
+ username: 'admin',
86
+ password: 'admin',
87
+ grantType: 'password',
88
+ clientId: 'admin-cli',
89
+ scope: 'offline_access', // Request offline access for refresh tokens
90
+ tokenLifeSpan: 60
91
+ });
92
+ ```
93
+
94
+ ### Automatic Token Refresh
95
+
96
+ When `tokenLifeSpan` is set, the client automatically refreshes the access token before expiration using an internal timer. The refresh happens at `tokenLifeSpan` seconds interval.
97
+
98
+ ### Error Handling
99
+
100
+ ```javascript
101
+ try {
102
+ await KeycloakManager.configure({
103
+ baseUrl: 'https://invalid-url',
104
+ realmName: 'master',
105
+ username: 'admin',
106
+ password: 'wrong-password',
107
+ grantType: 'password',
108
+ clientId: 'admin-cli'
109
+ });
110
+ } catch (error) {
111
+ console.error('Authentication failed:', error.message);
112
+ // Possible errors:
113
+ // - Network errors (connection refused, timeout)
114
+ // - Invalid credentials (401 Unauthorized)
115
+ // - Invalid realm or client (404 Not Found)
116
+ }
117
+ ```
118
+
119
+ ---
120
+
121
+ ## setConfig()
122
+
123
+ Update runtime configuration for the Keycloak Admin Client. Use this to switch realms or update settings after initial configuration.
124
+
125
+ **Syntax:**
126
+ ```javascript
127
+ KeycloakManager.setConfig(overrides)
128
+ ```
129
+
130
+ ### Parameters
131
+
132
+ #### overrides (Object) ⚠️ Required
133
+
134
+ Configuration overrides object.
135
+
136
+ | Property | Type | Required | Description |
137
+ |----------|------|----------|-------------|
138
+ | `realmName` | string | 📋 Optional | Switch to a different realm context |
139
+ | `requestConfig` | object | 📋 Optional | Axios request configuration overrides |
140
+
141
+ ### Returns
142
+
143
+ **void** - Synchronous operation
144
+
145
+ ### Examples
146
+
147
+ #### Switch Realm Context
148
+
149
+ ```javascript
150
+ // Initial configuration in master realm
151
+ await KeycloakManager.configure({
152
+ baseUrl: 'https://keycloak.example.com',
153
+ realmName: 'master',
154
+ username: 'admin',
155
+ password: 'admin',
156
+ grantType: 'password',
157
+ clientId: 'admin-cli'
158
+ });
159
+
160
+ // Now operate on a different realm
161
+ KeycloakManager.setConfig({ realmName: 'my-application-realm' });
162
+
163
+ // All subsequent operations use 'my-application-realm'
164
+ const users = await KeycloakManager.users.find({ max: 100 });
165
+ ```
166
+
167
+ #### Custom Request Configuration
168
+
169
+ ```javascript
170
+ KeycloakManager.setConfig({
171
+ realmName: 'my-realm',
172
+ requestConfig: {
173
+ timeout: 10000,
174
+ headers: {
175
+ 'X-Custom-Header': 'value'
176
+ }
177
+ }
178
+ });
179
+ ```
180
+
181
+ ### Notes
182
+
183
+ - `setConfig()` does NOT re-authenticate. It only updates the context.
184
+ - Changing `realmName` affects all subsequent API calls until changed again.
185
+ - The access token remains valid across realm switches (as long as the authenticated user/service account has permissions).
186
+
187
+ ---
188
+
189
+ ## getToken()
190
+
191
+ Retrieve the current access token. Useful for debugging or passing the token to other services.
192
+
193
+ **Syntax:**
194
+ ```javascript
195
+ const token = await KeycloakManager.getToken()
196
+ ```
197
+
198
+ ### Parameters
199
+
200
+ None
201
+
202
+ ### Returns
203
+
204
+ **Promise\<string\>** - The current access token (JWT)
205
+
206
+ ### Examples
207
+
208
+ ```javascript
209
+ await KeycloakManager.configure({
210
+ baseUrl: 'https://keycloak.example.com',
211
+ realmName: 'master',
212
+ username: 'admin',
213
+ password: 'admin',
214
+ grantType: 'password',
215
+ clientId: 'admin-cli'
216
+ });
217
+
218
+ const token = await KeycloakManager.getToken();
219
+ console.log('Access Token:', token);
220
+
221
+ // Use token with custom HTTP client
222
+ const axios = require('axios');
223
+ const response = await axios.get('https://keycloak.example.com/admin/realms/master', {
224
+ headers: {
225
+ 'Authorization': `Bearer ${token}`
226
+ }
227
+ });
228
+ ```
229
+
230
+ ### Notes
231
+
232
+ - The returned token is automatically refreshed by the internal timer (if `tokenLifeSpan` was configured).
233
+ - Token format is JWT (JSON Web Token).
234
+
235
+ ---
236
+
237
+ ## auth()
238
+
239
+ Re-authenticate with new or updated credentials. Use this to switch users or refresh authentication manually.
240
+
241
+ **Syntax:**
242
+ ```javascript
243
+ await KeycloakManager.auth(credentials)
244
+ ```
245
+
246
+ ### Parameters
247
+
248
+ #### credentials (Object) ⚠️ Required
249
+
250
+ Same structure as `configure()` credentials parameter.
251
+
252
+ ### Returns
253
+
254
+ **Promise\<void\>** - Resolves when re-authentication succeeds
255
+
256
+ ### Examples
257
+
258
+ #### Switch User
259
+
260
+ ```javascript
261
+ // Initial authentication as admin
262
+ await KeycloakManager.configure({
263
+ baseUrl: 'https://keycloak.example.com',
264
+ realmName: 'master',
265
+ username: 'admin',
266
+ password: 'admin',
267
+ grantType: 'password',
268
+ clientId: 'admin-cli'
269
+ });
270
+
271
+ // Later, re-authenticate as different user
272
+ await KeycloakManager.auth({
273
+ baseUrl: 'https://keycloak.example.com',
274
+ realmName: 'master',
275
+ username: 'realm-admin',
276
+ password: 'realm-admin-password',
277
+ grantType: 'password',
278
+ clientId: 'admin-cli'
279
+ });
280
+ ```
281
+
282
+ #### Refresh Expired Session
283
+
284
+ ```javascript
285
+ // If session expired or token invalidated
286
+ try {
287
+ await KeycloakManager.users.find();
288
+ } catch (error) {
289
+ if (error.response && error.response.status === 401) {
290
+ // Re-authenticate
291
+ await KeycloakManager.auth({
292
+ baseUrl: 'https://keycloak.example.com',
293
+ realmName: 'master',
294
+ username: 'admin',
295
+ password: 'admin',
296
+ grantType: 'password',
297
+ clientId: 'admin-cli'
298
+ });
299
+
300
+ // Retry operation
301
+ const users = await KeycloakManager.users.find();
302
+ }
303
+ }
304
+ ```
305
+
306
+ ### Notes
307
+
308
+ - `auth()` stops the existing token refresh timer and starts a new one (if `tokenLifeSpan` is configured).
309
+ - Use `auth()` instead of `configure()` when you need to change credentials without reinitializing handlers.
310
+
311
+ ---
312
+
313
+ ## stop()
314
+
315
+ Stop the automatic token refresh timer and cleanup resources. Call this when shutting down your application.
316
+
317
+ **Syntax:**
318
+ ```javascript
319
+ KeycloakManager.stop()
320
+ ```
321
+
322
+ ### Parameters
323
+
324
+ None
325
+
326
+ ### Returns
327
+
328
+ **void** - Synchronous operation
329
+
330
+ ### Examples
331
+
332
+ #### Application Shutdown
333
+
334
+ ```javascript
335
+ const KeycloakManager = require('keycloak-api-manager');
336
+
337
+ async function main() {
338
+ await KeycloakManager.configure({
339
+ baseUrl: 'https://keycloak.example.com',
340
+ realmName: 'master',
341
+ username: 'admin',
342
+ password: 'admin',
343
+ grantType: 'password',
344
+ clientId: 'admin-cli',
345
+ tokenLifeSpan: 60
346
+ });
347
+
348
+ // Do work
349
+ const users = await KeycloakManager.users.find({ max: 100 });
350
+ console.log(`Found ${users.length} users`);
351
+
352
+ // Cleanup before exit
353
+ KeycloakManager.stop();
354
+ console.log('Token refresh timer stopped');
355
+ }
356
+
357
+ main().catch(console.error);
358
+ ```
359
+
360
+ #### Process Signal Handlers
361
+
362
+ ```javascript
363
+ let cleanupDone = false;
364
+
365
+ process.on('SIGINT', () => {
366
+ if (!cleanupDone) {
367
+ console.log('Shutting down gracefully...');
368
+ KeycloakManager.stop();
369
+ cleanupDone = true;
370
+ process.exit(0);
371
+ }
372
+ });
373
+
374
+ process.on('SIGTERM', () => {
375
+ if (!cleanupDone) {
376
+ console.log('Shutting down gracefully...');
377
+ KeycloakManager.stop();
378
+ cleanupDone = true;
379
+ process.exit(0);
380
+ }
381
+ });
382
+ ```
383
+
384
+ ### Notes
385
+
386
+ - Always call `stop()` before application exit to prevent dangling timers.
387
+ - After calling `stop()`, you can call `configure()` or `auth()` again to restart.
388
+ - Not calling `stop()` may prevent Node.js process from exiting cleanly.
389
+
390
+ ---
391
+
392
+ ## Full Workflow Example
393
+
394
+ ```javascript
395
+ const KeycloakManager = require('keycloak-api-manager');
396
+
397
+ async function keycloakWorkflow() {
398
+ try {
399
+ // 1. Configure and authenticate
400
+ await KeycloakManager.configure({
401
+ baseUrl: 'https://keycloak.example.com:8443',
402
+ realmName: 'master',
403
+ username: 'admin',
404
+ password: 'admin',
405
+ grantType: 'password',
406
+ clientId: 'admin-cli',
407
+ tokenLifeSpan: 60
408
+ });
409
+ console.log('✓ Authenticated');
410
+
411
+ // 2. Work in master realm
412
+ const masterRealms = await KeycloakManager.realms.find();
413
+ console.log(`✓ Found ${masterRealms.length} realms`);
414
+
415
+ // 3. Switch to application realm
416
+ KeycloakManager.setConfig({ realmName: 'my-app' });
417
+ console.log('✓ Switched to my-app realm');
418
+
419
+ // 4. Perform operations
420
+ const users = await KeycloakManager.users.find({ max: 100 });
421
+ console.log(`✓ Found ${users.length} users in my-app`);
422
+
423
+ // 5. Get current token for debugging
424
+ const token = await KeycloakManager.getToken();
425
+ console.log('✓ Current token:', token.substring(0, 50) + '...');
426
+
427
+ // 6. Cleanup
428
+ KeycloakManager.stop();
429
+ console.log('✓ Stopped token refresh');
430
+
431
+ } catch (error) {
432
+ console.error('✗ Error:', error.message);
433
+ KeycloakManager.stop();
434
+ process.exit(1);
435
+ }
436
+ }
437
+
438
+ keycloakWorkflow();
439
+ ```
440
+
441
+ ---
442
+
443
+ ## See Also
444
+
445
+ - [API Reference](../api-reference.md) - Complete API documentation index
446
+ - [Realms API](realms.md) - Realm management operations
447
+ - [Users API](users.md) - User management operations
@@ -0,0 +1,129 @@
1
+ # Groups API
2
+
3
+ Group CRUD, subgroup tree navigation, role mappings, and fine-grained group permissions.
4
+
5
+ **Namespace:** `KeycloakManager.groups`
6
+
7
+ ## CRUD and Structure
8
+
9
+ ### create(groupRepresentation)
10
+ - **Required**: `groupRepresentation.name` (string)
11
+ - **Optional**: `path`, `attributes`, `subGroups`, `realmRoles`, `clientRoles`
12
+ - **Returns**: Promise<object>
13
+
14
+ ### find(filter)
15
+ - **Optional**: `search`, `first`, `max`, `briefRepresentation`, `exact`, `populateHierarchy`
16
+ - **Returns**: Promise<Array<GroupRepresentation>>
17
+
18
+ ### findOne(filter)
19
+ - **Required**: `filter.id` (string, group id)
20
+ - **Returns**: Promise<GroupRepresentation>
21
+
22
+ ### update(filter, groupRepresentation)
23
+ - **Required**: `filter.id` (string)
24
+ - **Required**: `groupRepresentation` (partial)
25
+ - **Returns**: Promise<void>
26
+
27
+ ### del(filter)
28
+ - **Required**: `filter.id` (string)
29
+ - **Returns**: Promise<void>
30
+
31
+ ### count(filter)
32
+ - **Optional**: `search`, `top`
33
+ - **Returns**: Promise<number>
34
+
35
+ ### listSubGroups(filter)
36
+ - **Required**: `filter.id` (string, parent group id)
37
+ - **Optional**: `search`, `first`, `max`, `briefRepresentation`
38
+ - **Returns**: Promise<Array<GroupRepresentation>>
39
+
40
+ ## Realm Role Mappings
41
+
42
+ ### addRealmRoleMappings(role_mapping)
43
+ - **Required**: `role_mapping.id` (group id)
44
+ - **Required**: `role_mapping.roles` (Array<{id,name}>)
45
+ - **Returns**: Promise<void>
46
+
47
+ ### delRealmRoleMappings(filters)
48
+ - **Required**: `filters.id` (group id)
49
+ - **Required**: `filters.roles` (Array<{id,name}>)
50
+ - **Returns**: Promise<void>
51
+
52
+ ### listRoleMappings(filters)
53
+ - **Required**: `filters.id` (group id)
54
+ - **Returns**: Promise<object>
55
+
56
+ ### listRealmRoleMappings(filters)
57
+ - **Required**: `filters.id` (group id)
58
+ - **Returns**: Promise<Array<RoleRepresentation>>
59
+
60
+ ### listAvailableRealmRoleMappings(filters)
61
+ - **Required**: `filters.id` (group id)
62
+ - **Returns**: Promise<Array<RoleRepresentation>>
63
+
64
+ ### listCompositeRealmRoleMappings(filters)
65
+ - **Required**: `filters.id` (group id)
66
+ - **Returns**: Promise<Array<RoleRepresentation>>
67
+
68
+ ## Client Role Mappings
69
+
70
+ ### addClientRoleMappings(filters)
71
+ - **Required**: `filters.id` (group id)
72
+ - **Required**: `filters.clientUniqueId` (client UUID)
73
+ - **Required**: `filters.roles` (Array<{id,name}>)
74
+ - **Returns**: Promise<void>
75
+
76
+ ### delClientRoleMappings(filters)
77
+ - **Required**: `filters.id` (group id)
78
+ - **Required**: `filters.clientUniqueId` (client UUID)
79
+ - **Required**: `filters.roles` (Array<{id,name}>)
80
+ - **Returns**: Promise<void>
81
+
82
+ ### listClientRoleMappings(filters)
83
+ - **Required**: `filters.id` (group id)
84
+ - **Required**: `filters.clientUniqueId` (client UUID)
85
+ - **Returns**: Promise<Array<RoleRepresentation>>
86
+
87
+ ### listAvailableClientRoleMappings(filters)
88
+ - **Required**: `filters.id` (group id)
89
+ - **Required**: `filters.clientUniqueId` (client UUID)
90
+ - **Returns**: Promise<Array<RoleRepresentation>>
91
+
92
+ ### listCompositeClientRoleMappings(filters)
93
+ - **Required**: `filters.id` (group id)
94
+ - **Required**: `filters.clientUniqueId` (client UUID)
95
+ - **Returns**: Promise<Array<RoleRepresentation>>
96
+
97
+ ## Fine-Grained Group Permissions (Wrapper Enhancement)
98
+
99
+ These methods wrap Keycloak management-permission endpoints used for group admin authorization.
100
+
101
+ ### setPermissions(filters, permissionRepresentation)
102
+ - **Required**: `filters.id` (group id)
103
+ - **Required**: `permissionRepresentation.enabled` (boolean)
104
+ - **Optional**: additional permission fields returned by Keycloak
105
+ - **Returns**: Promise<object>
106
+
107
+ ### listPermissions(filters)
108
+ - **Required**: `filters.id` (group id)
109
+ - **Returns**: Promise<object>
110
+
111
+ ### Feature Requirement
112
+ Use Keycloak with:
113
+
114
+ ```bash
115
+ --features=admin-fine-grained-authz:v1
116
+ ```
117
+
118
+ ## Example
119
+
120
+ ```js
121
+ const group = await KeycloakManager.groups.create({ name: 'engineering' });
122
+ await KeycloakManager.groups.setPermissions({ id: group.id }, { enabled: true });
123
+ const permissions = await KeycloakManager.groups.listPermissions({ id: group.id });
124
+ ```
125
+
126
+ ## See Also
127
+ - [API Reference](../api-reference.md)
128
+ - [Users](users.md)
129
+ - [Roles](roles.md)
@@ -0,0 +1,98 @@
1
+ # Identity Providers API
2
+
3
+ Manage identity providers (OIDC/SAML/social), mappers, import, and permissions.
4
+
5
+ **Namespace:** `KeycloakManager.identityProviders`
6
+
7
+ ## Provider CRUD
8
+
9
+ ### create(identityProvidersRepresentation)
10
+ - **Required**: `alias`, `providerId`
11
+ - **Optional**: `enabled`, `trustEmail`, `storeToken`, `firstBrokerLoginFlowAlias`, `config`
12
+ - **Returns**: Promise<object>
13
+
14
+ ### find()
15
+ - **Params**: none
16
+ - **Returns**: Promise<Array<IdentityProviderRepresentation>>
17
+
18
+ ### findOne(filter)
19
+ - **Required**: `filter.alias`
20
+ - **Returns**: Promise<IdentityProviderRepresentation>
21
+
22
+ ### update(filter, identityProviderRepresentation)
23
+ - **Required**: `filter.alias`
24
+ - **Required**: updated representation
25
+ - **Returns**: Promise<void>
26
+
27
+ ### del(filter)
28
+ - **Required**: `filter.alias`
29
+ - **Returns**: Promise<void>
30
+
31
+ ## Factory and Discovery
32
+
33
+ ### findFactory(filter)
34
+ - **Required**: `filter.providerId` (example: `oidc`, `saml`, `google`)
35
+ - **Returns**: Promise<object>
36
+
37
+ ### importFromUrl(filter)
38
+ - **Required**: provider-specific request payload (typically metadata URL fields)
39
+ - **Returns**: Promise<object>
40
+
41
+ ## Mappers
42
+
43
+ ### createMapper(mapperParams)
44
+ - **Required**: `mapperParams.identityProviderAlias`
45
+ - **Required**: `mapperParams.name`, `mapperParams.identityProviderMapper`
46
+ - **Optional**: `mapperParams.config`
47
+ - **Returns**: Promise<object>
48
+
49
+ ### findMappers(filter)
50
+ - **Required**: `filter.alias` (identity provider alias)
51
+ - **Returns**: Promise<Array<object>>
52
+
53
+ ### findOneMapper(filter)
54
+ - **Required**: `filter.alias`
55
+ - **Required**: `filter.id` (mapper id)
56
+ - **Returns**: Promise<object>
57
+
58
+ ### updateMapper(filter, mapperRepresentation)
59
+ - **Required**: `filter.alias`
60
+ - **Required**: `filter.id`
61
+ - **Required**: mapper representation
62
+ - **Returns**: Promise<void>
63
+
64
+ ### delMapper(filter)
65
+ - **Required**: `filter.alias`
66
+ - **Required**: `filter.id`
67
+ - **Returns**: Promise<void>
68
+
69
+ ## Permissions
70
+
71
+ ### updatePermission(filter, permissionRepresentation)
72
+ - **Required**: `filter.alias`
73
+ - **Required**: `permissionRepresentation.enabled` (boolean)
74
+ - **Returns**: Promise<object>
75
+
76
+ ### listPermissions(filter)
77
+ - **Required**: `filter.alias`
78
+ - **Returns**: Promise<object>
79
+
80
+ ## Example
81
+
82
+ ```js
83
+ await KeycloakManager.identityProviders.create({
84
+ alias: 'google',
85
+ providerId: 'google',
86
+ enabled: true,
87
+ config: {
88
+ clientId: process.env.GOOGLE_CLIENT_ID,
89
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET
90
+ }
91
+ });
92
+
93
+ const mappers = await KeycloakManager.identityProviders.findMappers({ alias: 'google' });
94
+ ```
95
+
96
+ ## See Also
97
+ - [API Reference](../api-reference.md)
98
+ - [Organizations](organizations.md)