Juri Adam
Juri Adam

Reputation: 569

Keycloak issues JWT token with outdated custom attributes from user storage SPI

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):

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

Answers (1)

Thomas
Thomas

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

Related Questions