Reputation: 569
Im using Keycloak version 19.0.1 together with a custom user storage SPI to load users from an existing legacy database. Im adding some custom attributes to the Keycloak user representation model via the mentioned user storage SPI. These custom attributes are part of the JWT token to be issued by Keycloak when calling /token
endpoint.
When calling /token
endpoint Keycloak issues the JWT token as expected with the custom attributes. However when a user in the legacy database is updated from the legacy app then keycloak still issues JWT tokens with outdated custom attribute values. I believe this is a caching issue in Keycloak, but was not able to find the correct caching option to disable cache and always get latest custom attribute values for the issued JWT token. Keycloak seems to support a variety of different local and distributed caches by default that can be configured with an XML file:
Im using default caching mode which according to the documentation is Infinispan. I have tried the following so far (see docker image attached below):
kc.sh build --spi-user-cache-infinispan-enabled=false
option when building keycloakKC_SPI_USER_CACHE_DEFAULT_ENABLED=false
during build phaseNO_CACHE
policy on user fedaration, but there seems to be a big and custom attributes are missing with this option in Keycloak 19 version: https://github.com/keycloak/keycloak/issues/10826 (therefore not really an option for me right now)What is the cache configuration I should use to always get latest custom attributes in Keycloak issued JWT token? Should I customize Infinispan configuration by passing XML file? Which cache configuration should change exactly?
Note: A manual workaround that helps is to invalidate all user sessions for the user in Keycloak. Afterwards keycloak always issues JWT token with latest custom attributes.
FROM <private-/gradle:7.1.0-jdk11 AS build
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
RUN gradle build --no-daemon
FROM <private-registry>/quay.io_keycloak/keycloak:19.0.1 as builder
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_DB=postgres
ENV KC_HTTP_RELATIVE_PATH="/auth"
ENV KC_HTTP_ENABLED=true
ENV KC_HOSTNAME_STRICT_HTTPS=false
ENV KC_HOSTNAME_STRICT=false
ENV KC_TRANSACTION_XA_ENABLED=false
ENV KC_SPI_USER_CACHE_DEFAULT_ENABLED=false
COPY --from=build /home/gradle/src/user-storage-spi/build/libs/*.jar /opt/keycloak/providers/
COPY /conf/quarkus.properties /opt/keycloak/conf/quarkus.properties
RUN /opt/keycloak/bin/kc.sh build --spi-user-cache-infinispan-enabled=false
FROM <private-registry>/quay.io_keycloak/keycloak:19.0.1
COPY --from=builder /opt/keycloak/ /opt/keycloak/
WORKDIR /opt/keycloak
ENV KC_HTTP_ENABLED=true
ENV KC_HOSTNAME_STRICT_HTTPS=false
ENV KC_HOSTNAME_STRICT=false
ENV KC_TRANSACTION_XA_ENABLED=false
ENV KC_SPI_USER_CACHE_DEFAULT_ENABLED=false
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
Upvotes: 0
Views: 858
Reputation: 26
Since the credential input validation is always executed in your implemented UserStorageProvider
, you can simply invalidate the user in the overwritten isValid()
method using the KeycloakSession
object:
public MyUserStorageProvider(KeycloakSession session, ComponentModel model) {
this.session = session;
this.model = model;
}
@Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput credentialInput) {
// invalidate user in cache here to ensure that custom user attributes are refreshed on every new request
session.invalidate(InvalidationHandler.ObjectType.USER, user.getId());
...
}
Upvotes: 1