keycloak-api-manager 3.2.1 → 4.0.1

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.
Files changed (53) hide show
  1. package/.env.example +27 -0
  2. package/Handlers/clientsHandler.js +240 -30
  3. package/Handlers/groupsHandler.js +16 -1
  4. package/Handlers/httpApiHelper.js +87 -0
  5. package/Handlers/realmsHandler.js +26 -4
  6. package/Handlers/usersHandler.js +43 -10
  7. package/README.md +341 -10
  8. package/index.js +159 -29
  9. package/package.json +3 -14
  10. package/test/.mocharc.json +4 -0
  11. package/test/TESTING.md +327 -0
  12. package/test/config/CONFIGURATION.md +170 -0
  13. package/test/config/default.json +36 -0
  14. package/test/config/local.json.example +7 -0
  15. package/test/config/secrets.json.example +7 -0
  16. package/test/diagnostic-protocol-mappers.js +189 -0
  17. package/test/docker-keycloak/DEPLOYMENT_GUIDE.md +262 -0
  18. package/test/docker-keycloak/certs/.gitkeep +7 -0
  19. package/test/docker-keycloak/docker-compose-https.yml +50 -0
  20. package/test/docker-keycloak/docker-compose.yml +59 -0
  21. package/test/docker-keycloak/setup-keycloak.js +501 -0
  22. package/test/enableServerFeatures.js +315 -0
  23. package/test/helpers/config.js +218 -0
  24. package/test/helpers/docker-helpers.js +513 -0
  25. package/test/helpers/setup.js +186 -0
  26. package/test/package.json +18 -0
  27. package/test/setup.js +194 -0
  28. package/test/specs/authenticationManagement.test.js +224 -0
  29. package/test/specs/clientCredentials.test.js +76 -0
  30. package/test/specs/clientScopes.test.js +388 -0
  31. package/test/specs/clients.test.js +791 -0
  32. package/test/specs/components.test.js +151 -0
  33. package/test/specs/debugClientLibrary.test.js +88 -0
  34. package/test/specs/groups.test.js +362 -0
  35. package/test/specs/identityProviders.test.js +292 -0
  36. package/test/specs/realms.test.js +390 -0
  37. package/test/specs/roles.test.js +322 -0
  38. package/test/specs/users.test.js +445 -0
  39. package/test/testConfig.js +69 -0
  40. package/.mocharc.json +0 -7
  41. package/docker-compose.yml +0 -27
  42. package/test/authenticationManagement.test.js +0 -329
  43. package/test/clientScopes.test.js +0 -256
  44. package/test/clients.test.js +0 -284
  45. package/test/components.test.js +0 -122
  46. package/test/config.js +0 -137
  47. package/test/docker-helpers.js +0 -111
  48. package/test/groups.test.js +0 -284
  49. package/test/identityProviders.test.js +0 -197
  50. package/test/mocha.env.js +0 -55
  51. package/test/realms.test.js +0 -349
  52. package/test/roles.test.js +0 -215
  53. package/test/users.test.js +0 -405
package/README.md CHANGED
@@ -201,6 +201,67 @@ Parameters:
201
201
  - refreshToken: [Optional] string containing a valid refresh token to request a new access token when using the refresh_token grant type.
202
202
  ---
203
203
 
204
+ ## 📚 Documentation Structure
205
+
206
+ This project maintains comprehensive documentation across multiple files. Here's a guide to find what you need:
207
+
208
+ ### 🚀 Getting Started
209
+ - **This file (README.md)** - Overview, installation, quick start, API reference
210
+ - **[DEPLOYMENT_GUIDE.md](./test/docker-keycloak/DEPLOYMENT_GUIDE.md)** - Deploy Keycloak locally or remotely with HTTP/HTTPS
211
+ - Interactive setup wizard (`npm run setup-keycloak`)
212
+ - Local vs remote deployments
213
+ - Certificate configuration
214
+ - Troubleshooting guide
215
+
216
+ ### 🧪 Testing & Development
217
+ - **[test/TESTING.md](./test/TESTING.md)** - Complete test suite documentation
218
+ - Test architecture (shared realm strategy)
219
+ - How to run tests
220
+ - Test file structure and components
221
+ - Writing new tests
222
+ - Performance metrics
223
+ - Troubleshooting test issues
224
+
225
+ - **[test/config/CONFIGURATION.md](./test/config/CONFIGURATION.md)** - Test configuration details
226
+ - Configuration file hierarchy (default.json → secrets.json → local.json)
227
+ - How to set up test environment
228
+ - Configuration schema
229
+ - Sensitive data management
230
+
231
+ ### 📋 Quick Reference
232
+
233
+ | Need | Location |
234
+ |------|----------|
235
+ | Install and basic usage | [README.md](./README.md#-installation) (this file) |
236
+ | Deploy Keycloak | [DEPLOYMENT_GUIDE.md](./test/docker-keycloak/DEPLOYMENT_GUIDE.md#quick-start) |
237
+ | Run tests locally | [test/TESTING.md](./test/TESTING.md#running-tests) |
238
+ | Configure tests | [test/config/CONFIGURATION.md](./test/config/CONFIGURATION.md) |
239
+ | API reference | [README.md](./README.md#-available-helper-functions) (below) |
240
+ | Test architecture | [test/TESTING.md](./test/TESTING.md#architecture) |
241
+
242
+ ---
243
+
244
+ ## 🧪 Testing Reminder
245
+
246
+ When running the test suite, two fields must always be resolvable from configuration (default/local/secrets/env/CLI):
247
+
248
+ - `keycloak.baseUrl`: target Keycloak URL used by admin client authentication
249
+ - `keycloak.realm`: realm used for authentication/context (usually `master`)
250
+
251
+ Notes:
252
+ - For **pre-deployed Keycloak** (`USE_REMOTE_KEYCLOAK=true`), these values must be set explicitly in test config.
253
+ - For **remote Docker via SSH**, `baseUrl` may be auto-overridden at runtime (for example via SSH tunnel), but defaults are still required as fallback.
254
+ - Keep this reminder in sync with `test/TESTING.md`.
255
+
256
+ Configuration file roles for tests:
257
+ - `test/config/default.json`: committed safe defaults
258
+ - `test/config/local.json`: git-ignored machine/environment overrides
259
+ - `test/config/secrets.json`: git-ignored sensitive values only
260
+
261
+ For full test configuration guidance see `test/config/CONFIGURATION.md`.
262
+
263
+ ---
264
+
204
265
  ## 🧰 Available Helper Functions
205
266
 
206
267
  ### `function setConfig(config)`
@@ -208,6 +269,117 @@ This function updates the runtime configuration of the Keycloak-api-manager Admi
208
269
  It allows switching the target realm, base URL, or HTTP request options without reinitializing the client or re-authenticating.
209
270
  It’s useful when you need to interact with multiple realms or environments dynamically using the same admin client instance.
210
271
 
272
+ **` -- @parameters -- `**
273
+ - config: is a JSON object that accepts the following parameters:
274
+ - realmName: [optional] The name of the target realm for subsequent API requests.
275
+ - baseUrl: [optional] The base URL of the Keycloak server (e.g., https://auth.example.com).
276
+ - requestOptions: [optional] Custom HTTP options (headers, timeout, etc.) applied to API calls.
277
+ - realmPath: [optional] A custom realm path if your Keycloak instance uses a non-standard realm route.
278
+ - other fields
279
+
280
+ **` -- @notes -- `**
281
+ Calling setConfig does not perform authentication
282
+
283
+ ---
284
+
285
+ ## 🧪 Test Workspace (Self-Contained)
286
+
287
+ The test environment is intentionally isolated inside the `test/` folder and uses a shared test realm approach for optimal performance.
288
+
289
+ ### Deploying Keycloak
290
+
291
+ There are two main approaches to deploy Keycloak for testing:
292
+
293
+ **1. Quick Start (Recommended)**
294
+
295
+ Use the interactive setup wizard:
296
+
297
+ ```bash
298
+ npm run setup-keycloak
299
+ ```
300
+
301
+ This script handles everything:
302
+ - ✅ Choose local or remote deployment
303
+ - ✅ Configure HTTP or HTTPS
304
+ - ✅ Auto-detect certificates in `test/docker-keycloak/certs/` (or prompt for custom path)
305
+ - ✅ Automatic certificate mounting (if HTTPS)
306
+ - ✅ Auto health checks and startup verification
307
+
308
+ **Note**: For HTTPS, place your certificates in `test/docker-keycloak/certs/` (named `keycloak.crt` and `keycloak.key`) to use them automatically without prompting.
309
+
310
+ **2. Manual Docker Compose**
311
+
312
+ Start Keycloak directly:
313
+
314
+ ```bash
315
+ docker-compose up -d # HTTP mode
316
+ npm test # Run tests
317
+ docker-compose down # Cleanup
318
+ ```
319
+
320
+ **For comprehensive deployment documentation** (cert generation, HTTPS setup, remote SSH deployments, troubleshooting):
321
+
322
+ 👉 See **[DEPLOYMENT_GUIDE.md](./test/docker-keycloak/DEPLOYMENT_GUIDE.md)**
323
+
324
+ ### Directory Structure
325
+
326
+ - `test/specs/` - All test suites (e.g., users.test.js, roles.test.js)
327
+ - `test/config/` - Configuration files managed by propertiesmanager
328
+ - See [test/config/CONFIGURATION.md](./test/config/CONFIGURATION.md) for details
329
+ - `test/node_modules/` - Test dependencies (isolated from package root)
330
+ - `test/setup.js` - Global Mocha hooks (runs before all tests)
331
+ - `test/enableServerFeatures.js` - Creates shared test realm infrastructure
332
+ - `test/testConfig.js` - Centralized configuration loader
333
+ - `test/diagnostic-protocol-mappers.js` - Manual protocol mapper diagnostic (not part of Mocha tests)
334
+ - `test/docker-keycloak/docker-compose.yml` - HTTP configuration
335
+ - `test/docker-keycloak/docker-compose-https.yml` - HTTPS configuration
336
+ - `test/docker-keycloak/setup-keycloak.js` - Interactive deployment script
337
+ - `test/docker-keycloak/certs/` - Default certificate location (keycloak.crt, keycloak.key)
338
+
339
+ For complete test architecture and structure details:
340
+
341
+ 👉 See **[test/TESTING.md](./test/TESTING.md)**
342
+
343
+
344
+ Configuration files are merged in order: `default.json` → `secrets.json` → `local.json`
345
+
346
+ ### Shared Test Realm
347
+
348
+ All tests use a **single shared realm** (`keycloak-api-manager-test-realm`) created once before any test runs. This approach:
349
+ - **Global Setup**: Runs once before all tests (via Mocha `before()` hook in `setup.js`)
350
+ - **Infrastructure Created**: Test realm, client, user, roles, groups, and client scopes
351
+ - **Performance**: Tests run ~5x faster than per-test realm creation approach
352
+ - **Isolation**: Each test creates unique resources to avoid conflicts across tests
353
+
354
+ ### Running Tests
355
+
356
+ ```bash
357
+ npm test # Run all tests
358
+ npm --prefix test test -- --grep "Users" # Run specific test suite
359
+ ```
360
+
361
+ **For detailed test information:**
362
+
363
+ 👉 See **[test/TESTING.md](./test/TESTING.md)** for:
364
+ - Test architecture and shared realm strategy
365
+ - Running tests with various configurations
366
+ - Writing new tests
367
+ - Troubleshooting and performance metrics
368
+
369
+ 👉 See **[test/config/CONFIGURATION.md](./test/config/CONFIGURATION.md)** for:
370
+ - Configuration file setup and management
371
+ - Setting up credentials (`secrets.json`)
372
+ - Configuration schema and options
373
+
374
+ ---
375
+
376
+ ## 🛠️ Available Helper Functions
377
+
378
+ ### `function setConfig(config)`
379
+ This function updates the runtime configuration of the Keycloak-api-manager Admin Client instance.
380
+ It allows switching the target realm, base URL, or HTTP request options without reinitializing the client or re-authenticating.
381
+ It's useful when you need to interact with multiple realms or environments dynamically using the same admin client instance.
382
+
211
383
  **` -- @parameters -- `**
212
384
  - config: is a JSON object that accepts the following parameters:
213
385
  - realmName: [optional] The name of the target realm for subsequent API requests.
@@ -297,6 +469,41 @@ try {
297
469
 
298
470
  ```
299
471
 
472
+ ### `function stop()`
473
+ This function cleanly stops the Keycloak Admin Client by clearing the automatic token refresh interval.
474
+ When the admin client is configured with `tokenLifeSpan`, it automatically refreshes the access token at regular intervals to maintain the session.
475
+ Calling `stop()` clears this interval, allowing your Node.js process to exit gracefully without hanging.
476
+
477
+ **` -- @returns -- `**
478
+ void
479
+
480
+ **` -- @notes -- `**
481
+ - This method should be called when you're done using the Keycloak Admin Client and want to terminate your application.
482
+ - It's particularly important in test environments or CLI scripts where the process needs to exit cleanly.
483
+ - If you don't call `stop()`, the token refresh interval may prevent the Node.js process from terminating.
484
+ - The method is safe to call multiple times; subsequent calls have no effect.
485
+
486
+ **` -- @example -- `**
487
+ ```js
488
+ const KeycloakManager = require('keycloak-api-manager');
489
+
490
+ // Configure and use the admin client
491
+ await KeycloakManager.configure({
492
+ baseUrl: 'http://localhost:8080',
493
+ realmName: 'master',
494
+ username: 'admin',
495
+ password: 'admin',
496
+ grantType: 'password',
497
+ tokenLifeSpan: 60
498
+ });
499
+
500
+ // Perform admin operations
501
+ const users = await KeycloakManager.users.find();
502
+
503
+ // Clean up and allow process to exit
504
+ KeycloakManager.stop();
505
+ ```
506
+
300
507
  ## 🔧 Available Admin Functions
301
508
  All administrative functions that rely on Keycloak's Admin API must be invoked using the
302
509
  KeycloakManager.{entity}.{function} pattern.
@@ -1426,6 +1633,8 @@ const KeycloakManager = require('keycloak-api-manager');
1426
1633
  ##### `function countGroups(filter)`
1427
1634
  Retrieves the number of groups that a given user is a member of.
1428
1635
 
1636
+ Compatibility note: this method now always returns a number. If the underlying API returns an object with a `count` field, it is normalized internally.
1637
+
1429
1638
  **` -- @parameters -- `**
1430
1639
  - filter is a JSON object that accepts filter parameters, such as { id: '' }
1431
1640
  - id: [required] The user ID of the user whose group membership count you want to retrieve.
@@ -1753,6 +1962,8 @@ Retrieves a list of offline sessions for the specified user.
1753
1962
  Offline sessions represent long-lived refresh tokens that allow clients to obtain new access tokens
1754
1963
  without requiring the user to be actively logged in.
1755
1964
 
1965
+ Compatibility note: when `clientId` is provided as the public client identifier, the library resolves it to the internal client UUID before querying offline sessions.
1966
+
1756
1967
  **` -- @parameters -- `**
1757
1968
  - filter is a JSON object that accepts this parameters:
1758
1969
  - id: [required] The ID of the user whose sessions will be listeds
@@ -1917,11 +2128,41 @@ const KeycloakManager = require('keycloak-api-manager');
1917
2128
  ```
1918
2129
 
1919
2130
 
1920
- ##### `function getUserStorageCredentialTypes()`
1921
- For more details, see the keycloak-admin-client package in the Keycloak GitHub repository.
2131
+ ##### `function getUserStorageCredentialTypes(filter)`
2132
+ Retrieves configured user-storage credential types for a specific user.
1922
2133
 
1923
- ##### `function updateCredentialLabel()`
1924
- For more details, see the keycloak-admin-client package in the Keycloak GitHub repository.
2134
+ **` -- @parameters -- `**
2135
+ - filter is a JSON object that accepts this parameters:
2136
+ - id: [required] The unique ID of the user.
2137
+ - realm: [optional] The realm name.
2138
+
2139
+ ```js
2140
+ const KeycloakManager = require('keycloak-api-manager');
2141
+
2142
+ const types = await KeycloakManager.users.getUserStorageCredentialTypes({
2143
+ id: 'user-id',
2144
+ });
2145
+ console.log('Configured credential types:', types);
2146
+ ```
2147
+
2148
+ ##### `function updateCredentialLabel(filter,label)`
2149
+ Updates the label of a specific credential for a user.
2150
+
2151
+ **` -- @parameters -- `**
2152
+ - filter is a JSON object that accepts this parameters:
2153
+ - id: [required] The unique ID of the user.
2154
+ - credentialId: [required] The unique ID of the credential.
2155
+ - realm: [optional] The realm name.
2156
+ - label: [required] String label to assign to the credential.
2157
+
2158
+ ```js
2159
+ const KeycloakManager = require('keycloak-api-manager');
2160
+
2161
+ await KeycloakManager.users.updateCredentialLabel(
2162
+ { id: 'user-id', credentialId: 'credential-id' },
2163
+ 'My Credential Label'
2164
+ );
2165
+ ```
1925
2166
 
1926
2167
 
1927
2168
 
@@ -2733,6 +2974,12 @@ This includes online sessions, not offline sessions (those are retrieved with li
2733
2974
  **` -- @parameters -- `**
2734
2975
  - filter: JSON structure that defines the filter parameters:
2735
2976
  - id: [required] The client ID whose session must be retrieved
2977
+
2978
+ **` -- @returns -- `**
2979
+ A number representing the count of active sessions for the specified client.
2980
+
2981
+ **` -- @notes -- `**
2982
+ The return value is automatically normalized to a number. In some Keycloak versions, the API may return an object with a `count` property, but this method handles that internally and always returns the numeric count directly.
2736
2983
 
2737
2984
  ```js
2738
2985
  const KeycloakManager = require('keycloak-api-manager');
@@ -2742,7 +2989,7 @@ const sessionCount = await KeycloakManager.clients.getSessionCount({
2742
2989
  id: 'internal-client-id'
2743
2990
  });
2744
2991
 
2745
- console.log(`Client internal-client-id has ${sessionCount.count} active sessions`);
2992
+ console.log(`Client internal-client-id has ${sessionCount} active sessions`);
2746
2993
 
2747
2994
  ```
2748
2995
 
@@ -2754,16 +3001,22 @@ without requiring active login.
2754
3001
  **` -- @parameters -- `**
2755
3002
  - filter: JSON structure that defines the filter parameters:
2756
3003
  - id: [required] The ID of the client for which you want to count offline sessions.
3004
+
3005
+ **` -- @returns -- `**
3006
+ A number representing the count of offline sessions for the specified client.
3007
+
3008
+ **` -- @notes -- `**
3009
+ The return value is automatically normalized to a number. In some Keycloak versions, the API may return an object with a `count` property, but this method handles that internally and always returns the numeric count directly.
2757
3010
 
2758
3011
  ```js
2759
3012
  const KeycloakManager = require('keycloak-api-manager');
2760
3013
 
2761
- // count active sessions
2762
- const sessionCount = await KeycloakManager.clients.getOfflineSessionCount({
3014
+ // count offline sessions
3015
+ const offlineSessionCount = await KeycloakManager.clients.getOfflineSessionCount({
2763
3016
  id: 'internal-client-id'
2764
3017
  });
2765
3018
 
2766
- console.log(`Client internal-client-id has ${sessionCount.count} offline sessions`);
3019
+ console.log(`Client internal-client-id has ${offlineSessionCount} offline sessions`);
2767
3020
 
2768
3021
  ```
2769
3022
 
@@ -3068,6 +3321,8 @@ console.log('Authorization scope details:', scope);
3068
3321
  The method is used to retrieve all resources associated with a specific authorization scope for a given client.
3069
3322
  This allows you to see which resources are governed by a particular scope in the client’s authorization settings.
3070
3323
 
3324
+ Compatibility note: this method uses a direct Keycloak Admin REST API call internally to avoid known normalization issues from `@keycloak/keycloak-admin-client` in some environments.
3325
+
3071
3326
  **` -- @parameters -- `**
3072
3327
  - filter: JSON structure that defines the filter parameters:
3073
3328
  - id: [required] The ID of the client to which the scope belongs
@@ -3144,6 +3399,8 @@ The method is used to import a resource into a client.
3144
3399
  This is part of Keycloak’s Authorization Services (UMA 2.0) and allows you to programmatically define
3145
3400
  resources that a client can protect with policies and permissions.
3146
3401
 
3402
+ Compatibility note: this method uses a direct Keycloak Admin REST API call internally to avoid inconsistent responses observed with `@keycloak/keycloak-admin-client` in some environments.
3403
+
3147
3404
  **` -- @parameters -- `**
3148
3405
  - filter: JSON structure that defines the filter parameters:
3149
3406
  - id: [required] The ID of the client to which the resource should be imported
@@ -3509,6 +3766,8 @@ console.log("Associated resources:", resources);
3509
3766
  The method is used to list all authorization scopes associated with a specific resource in a client’s resource server.
3510
3767
  This allows administrators to understand which scopes are directly linked to a protected resource and therefore which permissions can be applied to it.
3511
3768
 
3769
+ Compatibility note: this method uses a direct Keycloak Admin REST API call internally to avoid known normalization issues from `@keycloak/keycloak-admin-client` in some environments.
3770
+
3512
3771
  **` -- @parameters -- `**
3513
3772
  - filter: JSON structure that defines the filter parameters:
3514
3773
  - id: [required] The ID of the client (the resource server).
@@ -5287,9 +5546,12 @@ Groups help organize users and assign permissions in a scalable way
5287
5546
  ##### `function create(groupRepresentation)`
5288
5547
  Create a new group in the current realme
5289
5548
 
5549
+ Compatibility note: when `parentId` is provided in `groupRepresentation`, the group is created as a child of that parent.
5550
+
5290
5551
  **` -- @parameters -- `**
5291
5552
  - groupRepresentation:An object representing the new state of the group. You can update properties such as:
5292
5553
  - name: [optional] New name of the group
5554
+ - parentId: [optional] Parent group ID. If present, a child group is created under that parent.
5293
5555
  - attributes: [optional] Custom attributes up field
5294
5556
  - path: [optional] full path of the group
5295
5557
  - subGroups: [optional] List of child groups (can also be updated separately)
@@ -5300,6 +5562,12 @@ Create a new group in the current realme
5300
5562
  const KeycloakManager = require('keycloak-api-manager');
5301
5563
  // create a group called my-group
5302
5564
  KeycloakManager.groups.create({name: "my-group"});
5565
+
5566
+ // create a child group under an existing parent group
5567
+ await KeycloakManager.groups.create({
5568
+ name: "child-group",
5569
+ parentId: "parent-group-id"
5570
+ });
5303
5571
  ```
5304
5572
 
5305
5573
 
@@ -5356,6 +5624,8 @@ const group = await KeycloakManager.groups.del({ id: 'group-id' });
5356
5624
  Retrieves the total number of groups present in the specified realm.
5357
5625
  This is useful for pagination, reporting, or general statistics regarding group usage in a Keycloak realm.
5358
5626
 
5627
+ Compatibility note: this method always returns a number.
5628
+
5359
5629
  **` -- @parameters -- `**
5360
5630
  - filter: parameter provided as a JSON object that accepts the following filter:
5361
5631
  - realm: [optional] The name of the realm. If omitted, the default realm is used.
@@ -5364,11 +5634,11 @@ This is useful for pagination, reporting, or general statistics regarding group
5364
5634
  const KeycloakManager = require('keycloak-api-manager');
5365
5635
  // count groups
5366
5636
  const result = await KeycloakManager.groups.count();
5367
- console.log('Total groups:', result.count);
5637
+ console.log('Total groups:', result);
5368
5638
 
5369
5639
  // count groups with filter
5370
5640
  const result = await KeycloakManager.groups.count({ search: "cool-group" });
5371
- console.log('Total cool-group groups:', result.count);
5641
+ console.log('Total cool-group groups:', result);
5372
5642
 
5373
5643
  ```
5374
5644
 
@@ -6827,6 +7097,67 @@ const configDescription= await KeycloakManager.authenticationManagement.getConfi
6827
7097
  console.log("Configuration description:", configDescription);
6828
7098
  ```
6829
7099
 
7100
+ ---
7101
+
7102
+ ## 🔄 Recent Updates & Compatibility Notes
7103
+
7104
+ ### Version 3.2.0+ - Improvements and Bug Fixes
7105
+
7106
+ #### New Features
7107
+ - **`stop()` method**: Added a new method to cleanly stop the Keycloak Admin Client by clearing the automatic token refresh interval. This is essential for test environments and CLI scripts to allow the Node.js process to exit gracefully.
7108
+ ```js
7109
+ KeycloakManager.stop();
7110
+ ```
7111
+
7112
+ - **Token refresh interval now uses `unref()`**: The automatic token refresh interval is now unreferenced, allowing your Node.js process to exit even if the interval is still active, without needing to call `stop()` explicitly in simple scripts.
7113
+
7114
+ #### Bug Fixes & Normalizations
7115
+
7116
+ **Clients Handler:**
7117
+ - **`getSessionCount()` normalization**: Fixed inconsistent return values. The method now always returns a number directly instead of potentially returning an object with a `.count` property. This ensures consistent behavior across different Keycloak versions.
7118
+ ```js
7119
+ // Before: sessionCount could be { count: 5 } or 5
7120
+ // After: sessionCount is always 5
7121
+ const count = await KeycloakManager.clients.getSessionCount({ id: clientId });
7122
+ console.log(`Active sessions: ${count}`); // Always works
7123
+ ```
7124
+
7125
+ - **`getOfflineSessionCount()` normalization**: Same fix as `getSessionCount()` - now consistently returns a number.
7126
+
7127
+ - **`addClientScopeMappings()` and `delClientScopeMappings()` parameter fix**: These methods now properly accept the `roles` parameter as a second argument, making the API more consistent and easier to use.
7128
+ ```js
7129
+ // Correct usage
7130
+ await KeycloakManager.clients.addClientScopeMappings(
7131
+ { id: targetClientId, client: sourceClientId },
7132
+ [{ id: roleId, name: roleName }] // roles array as second parameter
7133
+ );
7134
+ ```
7135
+
7136
+ - **Direct API fallback for specific Authorization Services methods**: Some `@keycloak/keycloak-admin-client` methods can fail with normalization/response issues depending on Keycloak version. To guarantee reliable behavior, the following methods now use direct Keycloak Admin REST API calls internally:
7137
+ - `clients.listAllResourcesByScope()`
7138
+ - `clients.listScopesByResource()`
7139
+ - `clients.importResource()`
7140
+
7141
+ This keeps the public API unchanged while avoiding known library-level compatibility bugs.
7142
+
7143
+ **Users Handler:**
7144
+ - **`countGroups()` normalization**: this method now consistently returns a numeric value.
7145
+ - **`listOfflineSessions()` client ID compatibility**: public `clientId` values are resolved to internal UUID when needed.
7146
+ - **`getUserStorageCredentialTypes(filter)` and `updateCredentialLabel(filter,label)` signature fix**: both methods now expose the correct parameters and work as expected.
7147
+
7148
+ **Groups Handler:**
7149
+ - **`create(groupRepresentation)` parent support**: when `parentId` is provided, the group is correctly created as a child group.
7150
+ - **`count()` normalization**: this method now consistently returns a numeric value.
7151
+
7152
+ #### Testing
7153
+ - Comprehensive test suite added for all handlers (AuthenticationManagement, Clients, ClientScopes, Components, Groups, IdentityProviders, Realms, Roles, Users)
7154
+ - Tests include direct Keycloak Admin API verification to ensure library operations are correctly executed
7155
+ - Test coverage includes edge cases and error handling
7156
+
7157
+ These improvements ensure better compatibility across different Keycloak versions and more predictable behavior in production environments.
7158
+
7159
+ ---
7160
+
6830
7161
  ## 📝 License
6831
7162
 
6832
7163
  This project is licensed under the MIT License.