KeycloakCacheService.refreshUserCache over-evicts users sharing an id prefix #791

Closed
opened 2026-07-22 05:49:23 +00:00 by spikerj · 2 comments
Owner

Bug

KeycloakCacheService.refreshUserCache(userId) (libraries/keycloak-admin) evicts user-detail cache entries with key.startsWith(userId). The detail-cache keys are ${userId}_${userProfileMetadata}, so a bare prefix match over-evicts any other user whose id begins with the refreshed one — e.g. refreshUserCache("u1") also drops the cached entry for user "u12".

Impact

Correctness/efficiency: refreshing one user needlessly invalidates unrelated users that share an id prefix, causing redundant Keycloak round-trips (and a brief window where those users' cached data is gone). Not a data-integrity or security issue. In practice Keycloak ids are fixed-length UUIDs, so no id is a prefix of another and the bug is latent/not triggerable in production — but the code is wrong as written and trivially fixable.

Fix

Match on the ${userId}_ delimiter prefix instead of the bare id. Fixed + regression-tested in spikersoft-angular (same PR that added KeycloakCacheService coverage). The regression test was confirmed to fail against the old code and pass with the fix.

## Bug `KeycloakCacheService.refreshUserCache(userId)` (libraries/keycloak-admin) evicts user-detail cache entries with `key.startsWith(userId)`. The detail-cache keys are `${userId}_${userProfileMetadata}`, so a **bare prefix match over-evicts** any *other* user whose id begins with the refreshed one — e.g. `refreshUserCache("u1")` also drops the cached entry for user `"u12"`. ## Impact Correctness/efficiency: refreshing one user needlessly invalidates unrelated users that share an id prefix, causing redundant Keycloak round-trips (and a brief window where those users' cached data is gone). Not a data-integrity or security issue. In practice Keycloak ids are fixed-length UUIDs, so no id is a prefix of another and the bug is **latent/not triggerable in production** — but the code is wrong as written and trivially fixable. ## Fix Match on the `${userId}_` delimiter prefix instead of the bare id. Fixed + regression-tested in spikersoft-angular (same PR that added KeycloakCacheService coverage). The regression test was confirmed to fail against the old code and pass with the fix.
Author
Owner

Fixed in spikersoft-angular PR #530 (merged to master). refreshUserCache now matches the ${userId}_ delimiter prefix so refreshing one user can no longer evict a different user whose id shares the prefix. Includes a regression test verified to fail against the old code. Closing.

Fixed in spikersoft-angular PR #530 (merged to `master`). `refreshUserCache` now matches the `${userId}_` delimiter prefix so refreshing one user can no longer evict a different user whose id shares the prefix. Includes a regression test verified to fail against the old code. Closing.
Author
Owner

Resolved in spikersoft-angular PR #530 (merged to master, 1a3bad01): refreshUserCache now evicts on the ${userId}_ delimiter prefix instead of bare startsWith(userId), so refreshing one user can no longer drop a different user whose id begins with the same characters. Ships with a regression test verified to fail on the old code, plus 10 new KeycloakCacheService tests. Closing.

Resolved in spikersoft-angular PR #530 (merged to master, `1a3bad01`): `refreshUserCache` now evicts on the `${userId}_` delimiter prefix instead of bare `startsWith(userId)`, so refreshing one user can no longer drop a different user whose id begins with the same characters. Ships with a regression test verified to fail on the old code, plus 10 new KeycloakCacheService tests. Closing.
Sign in to join this conversation.