Keycloak 19.0.0 released

July 27 2022

To download the release go to Keycloak downloads.

Release notes

OpenID Connect and SAML Adapters End-of-life

Some Keycloak OpenID Connect adapters have reached end-of-life and are not included in this release.

Fuse 6 and 7 (OpenID Connect)

Keycloak will no longer be providing adapters for Fuse 6 or 7. If you need adapters for Fuse please leverage Red Hat Single Sign-On 7.x adapters.

JBoss AS 7 and EAP 6 (OpenID Connect and SAML)

JBoss AS 7 has been unmaintained for a very long time. If you are still using JBoss AS 7 we recommend migrating to WildFly and leveraging the native OIDC support in WildFly.

Red Hat customers using Red Hat JBoss Enterprise Application Platform 6.x should use Red Hat Single Sign-On 7.x adapters. These can be used in combination with the Keycloak server.

Jetty 9.2 and 9.3 (OpenID Connect and SAML)

Jetty 9.2 reached end of life in 2018, while Jetty 9.3 reached end of life in 2020. If you are still using these versions we recommend upgrading to Jetty 9.4 as soon as possible.

Spring Boot 1 (OpenID Connect)

Spring Boot 1.x reached end of life in 2019. If you are still using Spring Boot 1 we recommend upgrading to Spring Boot 2 as soon as possible.

WildFly legacy security layer (OpenID Connect and SAML)

In WildFly 25 the legacy security layer was removed, going forward only Elytron will be supported. We recommend anyone using an older version of WildFly to upgrade and leverage native OIDC support in WildFly.

Red Hat customers using Red Hat JBoss Enterprise Application Platform 7.x should use Red Hat Single Sign-On 7.x adapters. These can be used in combination with the Keycloak server.

New Admin Console graduation

The new Admin Console is now graduated to the default admin console, with the old console now deprecated. The old console will be removed in Keycloak 21.

Changes in Keycloak storage

The Keycloak storage is changing, and the current storage, while still supported, will eventually be replaced with a brand-new implementation. This change brings better support for cloud-native storages, no-downtime abilities, and better support for implementing custom storages for additional areas apart from users.

It means several deep changes in the supported features of the current store will become legacy features. The legacy store and the new store cannot be used simultaneously; only one store can be active at a time.

The most visible change is that the User Storage SPI is incompatible with the new storage API, the Map Storage API. Thus, the User Storage SPI will be deprecated with legacy store and will move to a separate module called keycloak-model-legacy. This change impacts several areas, especially areas related to user federation and custom user providers.

Furthermore, APIs have been consolidated so that the details of the storage layer will be transparent to the REST service layer. Specifically, the services will not be able to differentiate cached and non-cached objects, nor specifically access federated versus local storage.

Hence, custom extensions that access objects in local storage or cache through KeycloakSession methods must be reviewed. See Upgrading Guide for details.

OIDC Logout changes

In the previous release, we added support for OIDC logout. This release contains a few other fixes and polishing. The highlights include:

  • Support for the client_id parameter, which was added in recent draft of the OIDC RP-Initiated Logout specification. As a result, no need exists to use the Consent Required flag of the client to show the logout confirmation screen.

  • Configuration option Valid Post Logout Redirect URIs added to the OIDC client. This change is aligned with the OIDC specification, which allows you to use a different set of redirect URIs for redirect after login and logout. Value + used for Valid Post Logout Redirect URIs means that the logout will use the same set of redirect URIs as specified by the option of Valid Redirect URIs. This change also matches the default behavior when migrating from a previous version due to backwards compatibility.

For more details, see the Server Administration Guide.

Update Email Workflow

There is new preview feature UPDATE_EMAIL. When it is enabled and corresponding flag enabled in the realm, the users will be required to confirm updating their email by clicking the link, which will be sent to their new email address. For more details, see the Server Administration Guide. Thanks to Réda Housni Alaoui for the contribution.

Deprecated podDisruptionBudget in the legacy Keycloak Operator

With this release, we have deprecated podDisruptionBudget field in the Keycloak CR of the legacy Keycloak Operator. This optional field will be ignored when the Operator is deployed on Kubernetes version 1.25 and higher.

As a workaround, you can manually create the Pod Disruption Budget in your cluster, for example:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  labels:
    app: keycloak
  name: keycloak
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      component: keycloak

See also the Kubernetes Documentation.

Initial Support for centralized logging

Starting with version 19, Keycloak supports sending logs using GELF to centralized logging solutions like ELK, EFK or Graylog out of the box.

You can find the documentation and examples to get you up and running quickly in the logging guide

Migration from 18.0

Before you upgrade remember to backup your database. If you are not on the previous release refer to the documentation for a complete list of migration changes.

New Admin Console is now the default console

The new admin console is now the default console in Keycloak. If you are not able to start using the new admin console it is possible to continue to use the old admin console by disabling the new console, by for example running:

bin/kc.sh start-dev --features-disabled=admin2

An alternative approach to continue using the old admin console is to set the theme for the master realm or any other realm to keycloak.

As the new admin console is signficiantly different to the old admin console, is now based on React and uses a newer version of PatternFly, any custom themes will most likely have to be re-implemented from scratch. To create a custom theme for the new admin console the theme should extend keycloak.v2 instead of keycloak.

If you have explicitly set the admin console theme to keycloak for the master realm or any other realm, it will continue to use the old admin console. To update to the new admin console you need to change the theme to keycloak.v2.

The old admin console will be removed in Keycloak 21.

Changes to the server configuration and startup

Before this release, you would use the --auto-build when running the start command to tell the server to conditionally run a build if any build option has changed prior to starting the server.

In this release, the --auto-build flag is deprecated and you no longer need to use it to indicate that you want to set build options when starting the server. Instead, the server is always going to run a build by default prior to starting the server if any build option has changed. The new behavior improves the overall experience when configuring and starting the server by making it optional, although highly recommended, to run a build command beforehand in order to achieve the best startup time and memory footprint.

Now, in order to achieve the best startup time and memory footprint, set the --optimized option to disable the new default behavior. The --optimized flag tells the server that checking for and running a build directly as part of the startup is not needed:

kc.sh start --optimized

If you are already using a custom image to set build options and run an optimized Keycloak container, make sure you set the --optimized option when invoking the start command.

For more details, please take a look at the Configuration Guide and the Containers Guide.

Potentially breaking changes to the health endpoints

Before Keycloak 19.0.0, the quarkus based Keycloak distribution always enabled the following non-application endpoints unintentionally:

  • /q/health

  • /q/health/live

  • /q/health/ready

  • /q/metrics

Starting in Keycloak 19.0.0, these endpoints are disabled and a request will result in a 404 HTTP status-code. If you are using the /q/…​ endpoints, make sure to change your probes and monitoring systems to use the intended health endpoints instead when upgrading to Keycloak 19.0.0.

The intended health endpoints are:

  • /health

  • /health/live

  • /health/ready

  • /metrics

Apart from disabling the /q/ endpoints, these are the other improvements made to the health endpoints:

  • The health/live endpoint used for liveness probes is now decoupled from the database connections health, to match current good practices and to not have the same behaviour as the health/ready endpoint. As a result, the database check is not shown in the checks: array anymore when calling /health/live, so when there is a database hiccup, the liveness probe will still return HTTP status-code 200 and a status of UP, so no pod restart may be triggered.

  • The health/ready endpoint used for readiness probes still checks for a working database connection. Make sure you have not only health-enabled=true but also metrics-enabled=true set in your configuration, to enable the database check, resulting in an effective readiness probe. It will return HTTP status-code 503 and a status of DOWN when the database connection is not in a healthy state.

Expect more enhancements in this area in the future. For more information, see the Health guide

Changes using GELF / centralized log management

As stated in the release notes, Keycloak now supports gelf logging for centralized logging systems out of the box.

When you added the gelf related quarkus jars yourself in a prior version, make sure to switch to the supported configuration options in the logging guide and remove your jars from the providers folder.

Changes affecting developers

Keycloak undergoes large refactoring, which impacts existing code. Some of these changes require updates to existing code. These are in more detailed described below.

Rationale for changes

Keycloak has several limitations; for example, downtime is needed for upgrading a Keycloak cluster. To address the limitations, an in-depth refactor has been initiated.

The changes in this version are mostly attached to storage refactoring and a preparation of a new storage, called map storage. This storage will eventually replace the current storage, which will be called a legacy store with this version. The legacy store will still be available in Keycloak for several more versions.

The new store imposes a strict separation of responsibility between the service and storage layers. For that reason, the service layer’s visibility of an object’s origin will be restricted, so it will not be able to discriminate between cached or non-cached objects, or objects originating from local or federated storage.

User storage SPI will become deprecated. It will be supported for several more versions, but will be eventually replaced by the Map Storage SPI, which will offer the ability to create custom storages for any recognized area, such as users, roles, clients, or groups.

Extensions that rely on the level of detail available to services in the legacy store will need adjustment to retain this ability for the full deprecation period of the legacy store. The following section describes how that adjustment is accomplished.

Using a legacy and map store is mutually exclusive; one store cannot be used while the other is active.

Changes in the module structure

As part of introducing the new storage functionality, several public APIs around storage functionality in KeycloakSession have been consolidated, and some have been deprecated and will be removed in one of the next versions. Three new modules have been introduced, and data-oriented code from server-spi, server-spi-private, and services modules have been moved there:

org.keycloak:keycloak-model-legacy

Contains all public facing APIs from the legacy store, such as the User Storage API.

org.keycloak:keycloak-model-legacy-private

Contains private implementations that relate to user storage management, such as storage *Manager classes.

org.keycloak:keycloak-model-legacy-services

Contains all REST endpoints that directly operate on the legacy store, and have no meaning in the new store.

These modules will be available as long as legacy stores will be supported. After that period, they will be removed.

Changes in KeycloakSession

KeycloakSession has been simplified. Several methods have been deprecated in KeycloakSession and will be removed in a future version.

KeycloakSession session contains several methods for obtaining a provider for a particular object type, such as for a UserProvider there are users(), userLocalStorage(), userCache(), userStorageManager(), and userFederatedStorage(). This situaton may be confusing for the developer who has to understand the exact meaning of each method, and depends on current store layout. The new store does not distinguish federated from local storage.

For those reasons, only the users() method will be kept in KeycloakSession, and should replace all other calls listed above. The rest of the methods are deprecated, and will eventually be removed. The same pattern of deprecation applies to methods of other object areas, such as clients() or groups(). All methods ending in *StorageManager() and *LocalStorage() now throw an exception when being called, as there is no direct replacement in the new store. The next section describes how to migrate those calls to the new API or use the legacy API while using the old store.

The deprecated methods in KeycloakSession will be removed in a future release. The keycloak-model-legacy-* modules will be available for a longer time and will eventually be removed.

Migrating existing providers that do not depend on the legacy store

The existing providers need no migration if they do not call a deprecated method, which should be the case for most providers.

If the provider uses deprecated methods, but does not rely on local versus non-local storage, changing a call from the now deprecated userLocalStorage() to the method users() is the best option. Be aware that the semantics change here as the new method involves a cache if that has been enabled in the local setup.

Before migration: accessing a deprecated API that now throws an exception
session.userLocalStorage();
After migration: accessing the new API caller does not depend on the legacy storage API
session.users();
Migrating existing providers that depend on the legacy store

In the rare case when a custom provider needs to distinguish between the mode of a particular provider, access to the deprecated objects is provided by using the LegacyStoreManagers data store provider. This option will be available only if the legacy modules are part of the deployment.

Before migration: accessing a deprecated API that now throws an exception
session.userLocalStorage();
After migration: accessing the old functionality via the LegacyStoreManagers API
((LegacyDatastoreProvider) session.getProvider(DatastoreProvider.class)).userLocalStorage();

Some user storage related APIs have been wrapped in org.keycloak.storage.UserStorageUtil for convenience.

Creating custom storage providers

The API for creating a custom storage provider has not been fully stabilized yet, though it is available as a tech preview. See the MapStorageProvider SPI and its Javadoc for details. The availability of the new API is a priority for the next Keycloak version.

Changes to RealmModel

The methods getUserStorageProviders`, getUserStorageProvidersStream, getClientStorageProviders, getClientStorageProvidersStream, getRoleStorageProviders and getRoleStorageProvidersStream have been removed. Code which depends on these methods and runs with the legacy storage enabled should cast the instance as follows:

Before migration: code will not compile due to the changed API
realm.getClientStorageProvidersStream()...;
After migration: cast the instance to the legacy interface
((LegacyRealmModel) realm).getClientStorageProvidersStream()...;

Similarly, code that used to implement the interface RealmModel and wants to provide these methods should implement the new interface LegacyRealmModel. This interface is a sub-interface of RealmModel and includes the old methods:

Before migration: code implements the old interface
public class MyClass extends RealmModel {
    /* might not compile due to @Override annotations for methods no longer present
       in the interface RealmModel. /
    / ... */
}
After migration: code implements the new interface
public class MyClass extends LegacyRealmModel {
    /* ... */
}

Interface UserCache moved to the legacy module

As the caching status of objects will be trasparent to services, the interface UserCache has been moved to the module keycloak-legacy. Calls to session.userCache() will therefore return only a UserProvider, which is a breaking change.

Code that depends on the legacy implementation should access the UserCache directly. While such calls might be necessary while caching with the legacy store is used, it will not be necessary when using the new map store, as that one handles caching transparently.

Before migration: code will not compile due to a changed return type
// session.userCache() might return null, null-check omitted for brevity.
session.userCache().evict(realm, user);
After migration: use the API directly
// session.getProvider(UserCache.class) might return null, null-check omitted for brevity.
session.getProvider(UserCache.class).evict(realm, user);

To trigger the invalidation of a realm, instead of using the UserCache API, consider triggering an event:

Before migration: code will not compile due to a changed return type
UserCache cache = session.getProvider(UserCache.class);
if (cache != null) cache.clear();
After migration: use the invalidation API
session.invalidate(InvalidationHandler.ObjectType.REALM, realm.getId());

Credential management for users

Credentials for users were previously managed using session.userCredentialManager().method(realm, user, ...). The new way is to leverage user.credentialManager().method(...). This form gets the credential functionality closer to the API of users, and does not rely on prior knowledge of the user credential’s location in regard to realm and storage.

The old APIs have been deprecated, and will only work when the legacy storage is enabled in the deployment. The new APIs will work with both old and new storages.

Before migration: accessing a deprecated API
session.userCredentialManager().createCredential(realm, user, credentialModel)
After migration: accessing the new API
user.credentialManager().createStoredCredential(credentialModel)

For a custom UserStorageProvider, there is a new method credentialManager() that needs to be implemented when returning a UserModel. As those providers run in an environment with the legacy storage enabled, those must return an instance of the LegacyUserCredentialManager:

Before migration: code will not compile due to the new method credentialManager() required by UserModel
public class MyUserStorageProvider implements UserLookupProvider, ... {
    /* ... */
    protected UserModel createAdapter(RealmModel realm, String username) {
        return new AbstractUserAdapter(session, realm, model) {
            @Override
            public String getUsername() {
                return username;
            }
        };
    }
}
After migration: implementation of the API UserModel.credentialManager() for the legacy store.
public class MyUserStorageProvider implements UserLookupProvider, ... {
    /* ... */
    protected UserModel createAdapter(RealmModel realm, String username) {
        return new AbstractUserAdapter(session, realm, model) {
            @Override
            public String getUsername() {
                return username;
            }

            @Override
            public SubjectCredentialManager credentialManager() {
                return new LegacyUserCredentialManager(session, realm, this);
            }
        };
    }
}

Deprecated podDisruptionBudget in the legacy Keycloak Operator

With this release, we have deprecated podDisruptionBudget field in the Keycloak CR of the legacy Keycloak Operator. This optional field will be ignored when the Operator is deployed on Kubernetes version 1.25 and higher.

As a workaround, you can manually create the Pod Disruption Budget in your cluster, for example:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  labels:
    app: keycloak
  name: keycloak
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      component: keycloak

See also the Kubernetes Documentation.

Deployment changes in the new Keycloak Operator

The new Keycloak Operator now uses StatefulSet instead of Deployment for Keycloak deployments. There’s no automated migration in place given the Operator is a tech preview in this release. If you are using the new Operator with 18.0.z, please make sure to back up, delete and recreate your Keycloak CR after the upgrade to 19.0.0.

All resolved issues

New features

Enhancements

Bugs

Upgrading

Before you upgrade remember to backup your database and check the upgrade guide for anything that may have changed.