Upgrading Keycloak

This guide describes how to upgrade Keycloak. Use the following procedures in this order:

  1. Review the migration changes from the previous version of Keycloak.

  2. Upgrade the Keycloak server.

  3. Upgrade the Keycloak adapters.

  4. Upgrade the Keycloak Admin Client.

Migration Changes

Migrating to 24.0.3

Changes in redirect URI verification when using wildcards

Because of security concerns, the redirect URI verification now performs a exact string matching (no wildcard involved) if the passed redirect uri contains a userinfo part or its path accesses parent directory (/../).

The full wildcard * can still be used as a valid redirect in development for http(s) URIs with those characteristics. In production environments a exact valid redirect URI without wildcard needs to be configured for any URI of that type.

Please note that wildcard valid redirect URIs are not recommended for production and not covered by the OAuth 2.0 specification.

Changes to the org.keycloak.userprofile.UserProfileDecorator interface

To properly support multiple user storage providers within a realm, the org.keycloak.userprofile.UserProfileDecorator interface has changed.

The decorateUserProfile method is no longer invoked when parsing the user profile configuration for the first time (and caching it), but everytime a user is being managed through the user profile provider. As a result, the method changed its contract to:

List<AttributeMetadata> decorateUserProfile(String providerId, UserProfileMetadata metadata)

Differently than the previous contract and behavior, this method is only invoked for the user storage provider from where the user was loaded from.

Migrating to 24.0.2

Changes to Password Hashing

The release notes for Keycloak 24.0.0 have been updated with corrected description of the expected performance impact for the change, as well as sizing guide.

Migrating to 24.0.0

Changes to the Welcome theme

The 'welcome' theme has has been updated to use a new layout and now uses PatternFly 5, rather than PatternFly 3. If you are extending the theme, or providing your own, you may need to update it as follows:

Migrate from PatternFly 3 to PatternFly 5

The welcome theme was one of the more outdated themes in Keycloak. It was originally based on PatternFly 3, but has now been updated to use PatternFly 5, skipping a major version in the process. If your custom theme extends the built-in theme, you will need to update it to use PatternFly 5 syntax. Consult the PatternFly 5 documentation for details.

If you are still using PatternFly 3 in your own custom theme (not extending the built-in one), you can continue to use it, but PatternFly 3 support will be removed in a future release, so you should consider migrating to PatternFly 5 as soon as possible.

Automatic redirect to the Admin Console

If the Admin Console is enabled, the welcome page will automatically redirect to it if the administrative user already exists. This behavior can be modified by setting the redirectToAdmin in your theme.properties file. By default, the property is set to false, unless you are extending the built-in theme, in which case, the property is set to true.

These properties were previously used for navigational elements that are now no longer present. If you are extending the built-in theme, you will need to remove these properties from your theme.properties file, as they no longer have any effect.

Assets are now loaded from 'common' resources

Images such as the background, logo and favicon are now loaded from the 'common' resources, rather than the theme resources. This change means that if you are extending the built-in theme, and are overwriting these images, you will need to move them to the 'common' resources of your theme, and update your theme.properties file to include the new paths:

# This defaults to 'common/keycloak' if not set.

Changes to the Account Console theme customization

If you were previously extending the now deprecated version 2 of the Account Console theme, you will need to update your theme to use the new version 3 of the Account Console theme. The new version of the Account Console theme comes with some changes in regards to how it is customized. To start with a clean slate, you can follow the new customization quickstart.

To move your custom theme start by changing the parent to the new theme:

# Before

# After

If you have any custom React components, you will import React directly, rather than using a relative path:

// Before
import * as React from "../../../../common/keycloak/web_modules/react.js";

// After
import React from "react";

If you are using content.json to customize the theme there are some changes to the structure of the file, specifically:

  • The content property has been renamed to children.

  • The id, icon and componentName properties have been removed, as modulePath provides the same functionality.

Keycloak JS imports might need to be updated

If you are loading Keycloak JS directly from the Keycloak server, this section can be safely ignored. If you are loading Keycloak JS from the NPM package and are using a bundler like Webpack, Vite, and so on, you might need to make some changes to your code. The Keycloak JS package now uses the exports field in the package.json file. This means that you might have to change your imports:

// Before
import Keycloak from 'keycloak-js/dist/keycloak.js';
import AuthZ from 'keycloak-js/dist/keycloak-authz.js';

// After
import Keycloak from 'keycloak-js';
import AuthZ from 'keycloak-js/authz';

Features Changes

It is no longer allowed to have the same feature in both the --features and --features-disabled list. The feature should appear in only one list.

The usage of unversioned feature names, such as docker, in the --features list will allow for the most supported / latest feature version to be enabled for you. If you need more predictable behavior across releases, reference the particular version you want instead, such as docker:v1.

User Profile Changes

User profile enabled by default

The user profile feature is now enabled by default. The `declarative-user-profile`feature is no longer available, because the user profile is assumed to be enabled. Therefore, the User Profile Enabled switch is removed from the Realm Settings and replaced by Unmanaged attributes. When migrating from previous version, the behavior is as follows:

  • For the deployments where User Profile Enabled was set to ON, Unmanaged attributes will be set to OFF after the upgrade. As a result, only user attributes supported explicitly by user profile will be allowed.

  • For the deployments where the User Profile Enabled was set to OFF (which was also the default settings for the deployments with declarative-user-profile feature disabled, which was the default), Unmanaged attributes will be set to ON` after the upgrade. As a result, the behavior should be basically the same as in previous versions with the user profile disabled. The Attributes tab will remain in the user details part of the Admin Console. Also users can now set arbitrary attributes through UI pages such as the registration page and update profile page as long as a particular custom theme supports it. The custom themes should work as before as well. However, consider updating your themes to use the user-profile and maybe even remove your custom themes if those themes were need to add custom attributes. See the subsequent section on themes. Also, consider toggling Unmanaged attributes to OFF or enable this switch only for administrators so that you can rely on mainly on using managed attributes.

See the User Profile Documentation for the details about the Unmanaged attributes.

Default validations

Default user profile configuration comes with a set of default validations for the basic predefined fields. Those validations were not present in previous versions when the declarative-user-profile feature was disabled by default. If you have issues due to backward compatibility, you can change the default validators according to your needs. The default validators are as follows:

  • The`username`, email, firstName and lastName attributes have a maximum length of 255 characters. These validations were indirectly present in previous versions as well because of the database constraint on the table USER_ENTITY for those fields with a maximum length of 255 characters. However, when using user storage providers, it might be possible before to use longer values.

  • The username attribute has a minimum length of three characters. Username has also username-prohibited-characters and up-username-not-idn-homograph validator by default, which were not present in previous versions. See the Validation section of the User Profile Documentation for the details about those attributes. Note that username is not editable by default unless you have the realm switch Edit username enabled. This change means that existing users with incorrect usernames should still work and they will not be enforced to update their usernames. But new users will be enforced to use correct usernames during their registration or creation by the admin REST API.

  • The firstName and lastName attributes have the person-name-prohibited-characters validator on them, which were not present in previous versions. See the Validation section of the User Profile Documentation for the details about those attributes. Note that both first name and last name are editable by default, so users, who already have such incorrect first/last name from a previous version will be forced to update them when updating their user profiles.

User attribute names with strange characters

In previous versions, you could create a user with attribute names such as some:attribute or some/attribute. The user profile intentionally does not allow you to create attributes with such strange names in the user profile configuration. So you may need to configure Unmanaged attributes for your realm and enable unmanaged attributes for administrators (ideally) or for end users (if really needed). Although it is strongly preferred to avoid using such attribute names.

Verify Profile required action enabled by default

The verify-profile required action is enabled by default for new realms. However, when you migrate from a previous version, your existing realms will have the same state of this verify-profile action as before, which usually means disabled as it was disabled by default in previous versions. For the details about this required action, see the User Profile Documentation.

Breaking changes to the User Profile SPI

If you are using the User Profile SPI in your extension, you might be impacted by the API changes introduced in this release.

The org.keycloak.userprofile.Attributes interface includes the following changes:

  • Method getValues was renamed to get to make it more aligned with the same operation from a regular Java Map

  • Method isRootAttribute was moved to the utility class org.keycloak.userprofile.UserProfileUtil.isRootAttribute

  • Method getFirstValue was renamed to getFirst to make it less verbose

  • Method getReadable(boolean) was removed and now all attributes (including root attributes) are returned whenever they have read rights.

Changes to Freemarker templates to render pages based on the user profile and realm

In this release, the following templates were updated to make it possible to dynamically render attributes based on the user profile configuration set to a realm:

  • login-update-profile.ftl

  • register.ftl

  • update-email.ftl

These templates are responsible for rendering the update profile (when the Update Profile required action is enabled for a user), the registration, and the update email (when the UPDATE_EMAIL feature is enabled) pages, respectively.

If you use a custom theme to change these templates, they will function as expected because only the content is updated. However, we recommend you to take a look at how to configure a {declarative user profile} and possibly avoid changing the built-in templates by using all the capabilities provided by this feature.

Also, the templates used by the declarative-user-profile feature to render the pages for the same flows are longer necessary and removed in this release:

  • update-user-profile.ftl

  • register-user-profile.ftl

If you were using the declarative-user-profile feature in a previous release with customizations to the above templates, update the login-update-profile.ftl and register.ftl accordingly.

New Freemarker template for the update profile page at first login through a broker

In this release, the server will render the update profile page when the user is authenticating through a broker for the first time using the idp-review-user-profile.ftl template.

In previous releases, the template used to update the profile during the first broker login flow was the login-update-profile.ftl, the same used to update the profile when users are authenticating to a realm.

By using separate templates for each flow, a more clear distinction exist as to which flow a template is actually used rather than sharing a same template, and potentially introduce unexpected changes and behavior that should only affect pages for a specific flow.

If you have customizations to the login-update-profile.ftl template to customize how users update their profiles when authenticating through a broker, make sure to move your changes to the new template.

Truststore Changes

The spi-truststore-file-* options and the truststore related options https-trust-store-* are deprecated. Therefore, use the new default location for truststore material, conf/truststores, or specify your desired paths by using the truststore-paths option. For details, see the relevant guide.

The tls-hostname-verifier property should be used instead of the spi-truststore-file-hostname-verification-policy property.

A collateral effect of the changes is that now the truststore provider is always configured with some certificates (at least the default java trusted certificates are present). This new behavior can affect other parts of Keycloak.

For example, webauthn registration can fail if attestation conveyance was configured to Direct validation. Previously, if the truststore provider was not configured the incoming certificate was not validated. But now this validation is always performed. The registration fails with invalid cert path error as the certificate chain sent by the dongle is not trusted by Keycloak. The Certificate Authorities of the authenticator need to be present in the truststore provider to correctly perform the attestation.

Deprecated --proxy option

The --proxy option has been deprecated and will be removed in a future release. The following table explains how the deprecated option maps to supported options.

Deprecated usage New usage

kc.sh (no proxy option set)


kc.sh --proxy none


kc.sh --proxy edge

kc.sh --proxy-headers forwarded|xforwarded --http-enabled true

kc.sh --proxy passthrough

kc.sh --hostname-port 80|443 (depending if HTTPS is used)

kc.sh --proxy reencrypt

kc.sh --proxy-headers forwarded|xforwarded

For hardened security, the --proxy-headers option does not allow selecting both forwarded and xforwarded values at the same time (as it was the case before for --proxy edge and --proxy reencrypt).
When using the proxy headers option, make sure your reverse proxy properly sets and overwrites the Forwarded or X-Forwarded-* headers respectively. To set these headers, consult the documentation for your reverse proxy. Misconfiguration will leave Keycloak exposed to security vulnerabilities.

You can also set the proxy headers when using the Operator:

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
  name: example-kc
    headers: forwarded|xforwarded
If the proxy.headers field is not specified, the Operator falls back to the previous behavior by implicitly setting proxy=passthrough by default. This results in deprecation warnings in the server log. This fallback will be removed in a future release.

Changes to the user representation in both Admin API and Account contexts

Both org.keycloak.representations.idm.UserRepresentation and org.keycloak.representations.account.UserRepresentation representation classes have changed so that the root user attributes (such as username, email, firstName, lastName, and locale) have a consistent representation when fetching or sending the representation payload to the Admin and Account APIS, respectively.

The username, email, firstName, lastName, and locale attributes were moved to a new org.keycloak.representations.idm.AbstractUserRepresentation base class.

Also the getAttributes method is targeted for representing only custom attributes, so you should not expect any root attribute in the map returned by this method. This method is mainly targeted for clients when updating or fetching any custom attribute for a give user.

In order to resolve all the attributes including the root attributes, a new getRawAttributes method was added so that the resulting map also includes the root attributes. However, this method is not available from the representation payload and it is targeted to be used by the server when managing user profiles.

https-client-auth is a build time option

Option https-client-auth had been treated as a run time option, however this is not supported by Quarkus. The option needs to be handled at build time instead.

Sequential loading of offline sessions and remote sessions

Starting with this release, the first member of a Keycloak cluster will load remote sessions sequentially instead of in parallel. If offline session preloading is enabled, those will be loaded sequentially as well.

The previous code led to high resource-consumption across the cluster at startup and was challenging to analyze in production environments and could lead to complex failure scenarios if a node was restarted during loading. Therefore, it was changed to sequential session loading.

For offline sessions, the default in this and previous versions of Keycloak is to load those sessions on demand, which scales better with a lot of offline sessions than the attempt to preload them in parallel. Setups that use this default setup are not affected by the change of the loading strategy for offline sessions. Setups that have offline session preloading enabled should migrate to a setup where offline-session preloading is disabled.

Deprecated offline session preloading

The default behavior of Keycloak is to load offline sessions on demand. The old behavior to preload them at startup is now deprecated, as preloading them at startup does not scale well with a growing number of sessions, and increases Keycloak memory usage. The old behavior will be removed in a future release.

To re-enable old behavior while it is deprecated and not removed yet, use the feature flag and the SPI option as shown below:

bin/kc.[sh|bat] start --features-enabled offline-session-preloading --spi-user-sessions-infinispan-preload-offline-sessions-from-database=true

The API of UserSessionProvider deprecated the method getOfflineUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId). Instead of this method, use getOfflineUserSessionByBrokerUserIdStream(RealmModel, String brokerUserId) to first get the sessions of a user, and then filter by the broker session ID as needed.

Infinispan metrics use labels for cache manager and cache names

When enabling metrics for Keycloak’s embedded caches, the metrics now use labels for the cache manager and the cache names.

Old metric example without labels
New metric example with labels

To revert the change for an installation, use a custom Infinispan XML configuration and change the configuration as follows:

<metrics names-as-tags="false" />

User attribute value length extension

As of this release, Keycloak supports storing and searching by user attribute values longer than 255 characters, which was previously a limitation.

In setups where users are allowed to update attributes, for example, via the account console, prevent denial of service attacks by adding validations. Ensure that no unmanaged attributes are allowed and all editable attributes have a validation that limits the input length.

For unmanaged attributes, the maximum length is 2048 characters. For managed attributes, the default maximum length is 2048 characters. Administrator can change this by adding a validator of type length.

Keycloak caches user-related objects in its internal caches. Using longer attributes increases the memory that is consumed by the cache. Therefore, limiting the size of the length attributes is recommended. Consider storing large objects outside Keycloak and reference them by ID or URL.

This change adds new indexes on the tables USER_ATTRIBUTE and FED_USER_ATTRIBUTE. If those tables contain more than 300000 entries, Keycloak will skip the index creation by default during the automatic schema migration and instead log the SQL statement on the console during migration to be applied manually after Keycloak’s startup. See the Upgrading Guide for details on how to configure a different limit.

Additional migration steps for LDAP

This is for installations that match all the following criteria:

  • User attributes in the LDAP directory are larger than 2048 characters or binary attributes that are larger than 1500 bytes.

  • The attributes are changed by admins or users via the admin console, the APIs or the account console.

To be able to enable changing those attributes via UI and REST APIs, perform the following steps:

  1. Declare the attributes identified above as managed attributes in the user profile of the realm.

  2. Define a length validator for each attribute added in the previous step specifying the desired minimum and maximum length of the attribute value. For binary values, add 33 percent to the expected binary length to count in the overhead for Keycloak’s internal base64 encoding of binary values.

Additional migration steps for custom user storage providers

This is for installations that match all the following criteria:

  • Running MariaDB or MySQL as a database for Keycloak.

  • Entries in table FED_USER_ATTRIBUTE with contents in the VALUE column that are larger than 2048 characters. This table is used for custom user providers which have federation enabled.

  • The long attributes are changed by admins or users via the admin console or the account console.

To be able to enable changing those attributes via UI and REST APIs, perform the following steps:

  1. Declare the attributes identified above as managed attributes in the user profile of the realm.

  2. Define a length validator for each attribute added in the previous step specifying the desired minimum and maximum length of the attribute value.

The Admin send-verify-email API now uses the same email verification template

PUT /admin/realms/{realm}/users/{id}/send-verify-email

In this release, the API will use the email-verification.ftl template instead of executeActions.ftl.

Before upgrading
Perform the following action(s): Verify Email
After upgrading
Confirm validity of e-mail address email@example.org.

If you have customized the executeActions.ftl template to modify how users verify their email using this API, ensure that you transfer your modifications to the new template.

A new parameter called lifespan will be introduced to allow overriding of the default lifespan value (12 hours).

If you prefer the previous behavior, use the execute-actions-email API as follows.

PUT /admin/realms/{realm}/users/{id}/execute-actions-email


Removal of the deprecated mode for SAML encryption

The compatibility mode for SAML encryption introduced in version 21 is now removed. The system property keycloak.saml.deprecated.encryption is not managed anymore by the server. The clients which still used the old signing key for encryption should update it from the new IDP configuration metadata.

Changes to Password Hashing

In this release, we adapted the password hashing defaults to match the OWASP recommendations for Password Storage.

As part of this change, the default password hashing provider has changed from pbkdf2-sha256 to pbkdf2-sha512. Also, the number of default hash iterations for pbkdf2 based password hashing algorithms changed as follows:

Provider ID Algorithm Old Iterations New Iterations













If a realm does not explicitly configure a password policy with hashAlgorithm and hashIterations, then the new configuration will take effect on the next password based login, or when a user password is created or updated.

Performance of new password hashing configuration

Tests on a machine with an Intel i9-8950HK CPU (12) @ 4.800GHz yielded the following ⌀ time differences for hashing 1000 passwords (averages from 3 runs). Note that the average duration for the PBKDF2WithHmacSHA1 was computed with a lower number of passwords due to the long runtime.

Provider ID Algorithm Old duration New duration Difference
















Users of the pbkdf2 provider might need to explicitly reduce the number of hash iterations to regain acceptable performance. This can be done by configuring the hash iterations explicitly in the password policy of the realm.

Expected increased overall CPU usage and temporary increased database activity

The Concepts for sizing CPU and memory resources in the Keycloak High Availability guide have been updated to reflect the new hashing defaults. The CPU usage per password-based login in our tests increased by the factor of five, which includes both the changed password hashing and unchanged TLS connection handling. The overall CPU increase should be around the factor of two to three due to the averaging effect of Keycloak’s other activities like refreshing access tokens and client credential grants. Still, this depends on the unique workload of an installation.

After the upgrade, during a password-based login, the user’s passwords will be re-hashed with the new hash algorithm and hash iterations as a one-off activity and updated in the database. As this clears the user from Keycloak’s internal cache, you will also see an increased read activity on the database level. This increased database activity will decrease over time as more and more user’s passwords have been re-hashed.

How to keep using the old pbkdf2-sha256 password hashing?

To keep the old password hashing for a realm, specify hashAlgorithm and hashIterations explicitly in the realm password policy.

  • Hashing Algorithm: pbkdf2-sha256

  • Hashing Iterations: 27500

Operator Referenced Resource Polling

Secrets and ConfigMaps referenced via the Keycloak CR will now be polled for changes, rather than watched via the api server. It may take around 1 minute for changes to be detected.

This was done so to not require label manipulation on those resources. After upgrading if any Secret still has the operator.keycloak.org/component label, it may be removed or ignored.

Renaming JPA provider configuration options for migration

After removal of the Map Store the following configuration options were renamed:

  • spi-connections-jpa-legacy-initialize-empty to spi-connections-jpa-quarkus-initialize-empty

  • spi-connections-jpa-legacy-migration-export to spi-connections-jpa-quarkus-migration-export

  • spi-connections-jpa-legacy-migration-strategy to spi-connections-jpa-quarkus-migration-strategy

Renaming model modules

After removal of the Map Store the following modules were renamed:

  • org.keycloak:keycloak-model-legacy-private to org.keycloak:keycloak-model-storage-private

  • org.keycloak:keycloak-model-legacy-services to org.keycloak:keycloak-model-storage-services

and org.keycloak:keycloak-model-legacy module was deprecated and will be removed in the next release in favour of org.keycloak:keycloak-model-storage module.

Temporary lockout log replaced with event

There is now a new event USER_DISABLED_BY_TEMPORARY_LOCKOUT when a user is temporarily locked out by the brute force protector. The log with ID KC-SERVICES0053 has been removed as the new event offers the information in a structured form.

As it is a success event, the new event is logged by default at the DEBUG level. Use the setting spi-events-listener-jboss-logging-success-level as described in the Event listener chapter in the Server Administration Guide to change the log level of all success events.

To trigger custom actions or custom log entries, write a custom event listener as described in the Event Listener SPI in the Server Developer Guide.

Operator Customization Property Keys

The property keys used by the operator for advanced configuration have changed from operator.keycloak to kc.operator.keycloak.

Keycloak CR resources options

When no resources options are specified in the Keycloak CR and KeycloakRealmImport CR, default values are used. The default requests memory for Keycloak deployment and the realm import Job is set to 1700MiB, and the limits memory is set to 2GiB.

Updates to cookies

As part of refactoring cookie handling in Keycloak there are some changes to how cookies are set:

  • All cookies will now have the secure attribute set if the request is through a secure context

  • WELCOME_STATE_CHECKER cookies now set SameSite=Strict

For custom extensions there may be some changes needed:

  • LocaleSelectorProvider.KEYCLOAK_LOCALE is deprecated as cookies are now managed through the CookieProvider

  • HttpResponse.setWriteCookiesOnTransactionComplete has been removed

  • HttpCookie is deprecated, please use NewCookie.Builder instead

  • ServerCookie is deprecated, please use NewCookie.Builder instead

Internal algorithm changed from HS256 to HS512

The algorithm that Keycloak uses to sign internal tokens (a JWT which is consumed by Keycloak itself, for example a refresh or action token) is being changed from HS256 to the more secure HS512. A new key provider named hmac-generated-hs512 is now added for realms. Note that in migrated realms the old hmac-generated provider and the old HS256 key are maintained and still validate tokens issued before the upgrade. The HS256 provider can be manually deleted when no more old tokens exist following the rotating keys guidelines.

GELF log handler has been deprecated

With sunsetting of the underlying library providing integration with GELF, Keycloak will no longer support the GELF log handler out-of-the-box. This feature will be removed in a future release. If you require an external log management, consider using file log parsing.

Migrating to 23.0.5

Changes in jboss-logging event messages

Because of issue #25078, the jboss-logging message values are now quoted (character " by default) and sanitized to prevent any line break. There are two new options in the provider (spi-events-listener-jboss-logging-sanitize and spi-events-listener-jboss-logging-quotes) that allow you to customize the new behavior. For example, to avoid both sanitization and quoting, the server can be started in this manner:

./kc.sh start --spi-events-listener-jboss-logging-sanitize=false --spi-events-listener-jboss-logging-quotes=none ...

For more information about the options, see all provider configuration guide.

Migrating to 23.0.4

Fix handling of Groups.getSubGroups briefRepresentation parameter

Version 23.0.0 introduced a new endpoint getSubGroups ("children") on the Groups resource, where the meaning of the parameter briefRepresentation meant the retrieval of full representations of the sub groups. The meaning is now changed to return the brief representation.

Migrating to 23.0.2

Valid redirect URIs for clients are always compared with exact string matching

Version 1.8.0 introduced a lower-case for the hostname and scheme when comparing a redirect URI with the specified valid redirects for a client. Unfortunately it did not fully work in all the protocols, and, for example, the host was lower-cased for http but not for https. As OAuth 2.0 Security Best Current Practice advises to compare URIs using exact string matching, Keycloak will follow the recommendation and for now on valid redirects are compared with exact case even for the hostname and scheme.

For realms relying on the old behavior, the valid redirect URIs for their clients should now hold separate entries for each URI that should be recognized by the server.

Although it introduces more steps and verbosity when configuring clients, the new behavior enables more secure deployments as pattern-based checks are frequently the cause of security issues. These issues are due to how they are implemented and how they are configured.

Operator -secrets-store Secret

Older versions of the operator created a Secret to track watched Secrets. Newer versions of the operator no longer use the -secrets-store Secret, so it may be deleted.

If you are on 23.0.0 or 23.0.1 and see "org.keycloak.operator.controllers.KeycloakAdminSecretDependentResource → java.lang.IllegalStateException: More than 1 secondary resource related to primary" in the operator log then either delete the -secrets-store Secret, or upgrade to 23.0.2 where this is no longer an issue.

Migrating to 23.0.0

Added iss parameter to OAuth 2.0/OpenID Connect Authentication Response

RFC 9207 OAuth 2.0 Authorization Server Issuer Identification specification adds the parameter iss in the OAuth 2.0/OpenID Connect Authentication Response for realizing secure authorization responses.

In past releases, we did not have this parameter, but now Keycloak adds this parameter by default, as required by the specification.

However, some OpenID Connect / OAuth2 adapters, and especially older Keycloak adapters, may have issues with this new parameter.

For example, the parameter will be always present in the browser URL after successful authentication to the client application. In these cases, it may be useful to disable adding the iss parameter to the authentication response. This can be done for the particular client in the Keycloak Admin Console, in client details in the section with OpenID Connect Compatibility Modes, described in Compatibility with older adapters. Dedicated Exclude Issuer From Authentication Response switch exists, which can be turned on to prevent adding the iss parameter to the authentication response.

Wildcard characters handling

JPA allows wildcards % and _ when searching, while other providers like LDAP allow only *. As * is a natural wildcard character in LDAP, it works in all places, while with JPA it only worked at the beginning and the end of the search string. Starting with this release the only wildcard character is * which work consistently across all providers in all places in the search string. All special characters in a specific provider like % and _ for JPA are escaped. For exact search, with added quotes such as "w*ord", the behavior remains the same as in previous releases.

Language files for themes default to UTF-8 encoding

This release now follows the standard mechanisms of Java and later, which assumes resource bundle files to be encoded in UTF-8.

Previous versions of Keycloak supported specifying the encoding in the first line with a comment like # encoding: UTF-8, which is no longer supported and is ignored.

Message properties files for themes are now read in UTF-8 encoding, with an automatic fallback to ISO-8859-1 encoding. If you are using a different encoding, convert the files to UTF-8.

Changes to the value format of claims mapped by the realm and client role mappers

Before this release, both realm (User Realm Role) and client (User Client Role) protocol mappers were mapping a stringfied JSON array when the Multivalued setting was disabled.

However, the Multivalued setting indicates whether the claim should be mapped as a list or, if disabled, only a single value from the same list of values.

In this release, the role and client mappers now map to a single value from the effective roles of a user when they are marked as single-valued (Multivalued disabled).

Changes to password fields in Login UI

In this version we want to introduce a toggle to hide/show password inputs.

Affected pages:
  • login.ftl

  • login-password.ftl

  • login-update-password.ftl

  • register.ftl

  • register-user-profile.ftl

In general all <input type="password" name="password" /> are encapsulated within a div now. The input element is followed by a button which toggles the visibility of the password input.

Old code example:

<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>

New code example:

<div class="${properties.kcInputGroup!}">
    <input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
    <button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
            aria-controls="password" data-password-toggle
            data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
        <i class="fa fa-eye" aria-hidden="true"></i>

Default Keycloak CR Hostname

When running on OpenShift, with ingress enabled, and with the spec.ingress.classname set to openshift-default, you may leave the spec.hostname.hostname unpopulated in the Keycloak CR. The operator will assign a default hostname to the stored version of the CR similar to what would be created by an OpenShift Route without an explicit host - that is ingress-namespace.appsDomain If the appsDomain changes, or should you need a different hostname for any reason, then update the Keycloak CR.

The deprecated auto-build CLI option was removed

The auto-build CLI option has been marked as deprecated for a long time. In this release, it was completely removed, and it is no longer supported.

When executing the start command, the server is automatically built based on the configuration. In order to prevent this behavior, set the --optimized flag.

kc.sh and shell metacharacters

The kc.sh no longer uses an additional shell eval on parameters and the environment variables JAVA_OPTS_APPEND and JAVA_ADD_OPENS, thus the continued use of double escaping/quoting will result in the parameter being misunderstood. For example instead of

bin/kc.sh start --db postgres --db-username keycloak --db-url "\"jdbc:postgresql://localhost:5432/keycloak?ssl=false&connectTimeout=30\"" --db-password keycloak --hostname localhost

Use a single escape:

bin/kc.sh start --db postgres --db-username keycloak --db-url "jdbc:postgresql://localhost:5432/keycloak?ssl=false&connectTimeout=30" --db-password keycloak --hostname localhost

This change also means you cannot invoke kc.sh using a single quoted value of all arguments. For example you can no longer use

bin/kc.sh "start --help"

it must instead be individual arguments

bin/kc.sh start --help

Similarly instead of

bin/kc.sh build "--db postgres"

it must instead be individual arguments

bin/kc.sh build --db postgres

The usage of individual arguments is also required in Dockerfile run commands.

Removed RegistrationProfile form action

The form action RegistrationProfile (displayed in the UI of authentication flows as Profile Validation) was removed from the codebase and also from all authentication flows. By default, it was in the built-in registration flow of every realm. The validation of user attributes as well as creation of the user including all that user’s attributes is handled by RegistrationUserCreation form action and hence RegistrationProfile is not needed anymore. There is usually no further action needed in relation to this change, unless you used RegistrationProfile class in your own providers.

Deprecated methods from data providers and models

  • RealmModel#getTopLevelGroupsStream() and overloaded methods are now deprecated

GroupProvider changes

A new method has been added to allow for searching and paging through top level groups. If you implement this interface you will need to implement the following method:

Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm,
                                           String search,
                                           Boolean exact,
                                           Integer firstResult,
                                           Integer maxResults)

GroupRepresentation changes

  • new field subGroupCount added to inform client how many subgroups are on any given group

  • subGroups list is now only populated on queries that request hierarchy data

  • This field is populated from the "bottom up" so cannot be relied on for getting all subgroups for a group. Use a GroupProvider or request the subgroups from GET {keycloak server}/realms/{realm}/groups/{group_id}/children

New endpoint for Group Admin API

Endpoint GET {keycloak server}/realms/{realm}/groups/{group_id}/children added as a way to get subgroups of specific groups that support pagination

RESTEeasy Reactive

Relying on RESTEasy Classic is not longer an option because it is not available anymore. Migration will be needed for SPI’s and code that is relying on RESTEasy Classic and related packages part of org.jboss.resteasy.spi.*.

Partial export requires manage-realm permission

The endpoint POST {keycloak server}/realms/{realm}/partial-export and the corresponding action in the admin console now require manage-realm permission for execution instead of view-realm. This endpoint exports the realm configuration into a JSON file and the new permission is more appropriate. The parameters exportGroupsAndRoles and exportClients, which include the realm groups/roles and clients in the export respectively, continue managing the same permissions (query-groups and view-clients).

Removal of the options to trim the event’s details length

Since this release, Keycloak supports long value for EventEntity details column. Therefore, it no longer supports options for trimming event detail length --spi-events-store-jpa-max-detail-length and --spi-events-store-jpa-max-field-length.

User Profile updates

This release includes many fixes and updates that are related to user profile as we are working on promoting this feature from preview to officially supported. Minor changes exist for the SPI such as the newly added method boolean isEnabled(RealmModel realm) on UserProfileProvider interface. Also some user profile classes and some validator related classes (but not builtin validator implementations) were moved from keycloak-server-spi-private to keycloak-server-spi module. However, the packages for java classes remain the same. You might be affected in some corner cases, such as when you are overriding the built-in implementation with your own UserProfileProvider implementation. However, note that UserProfileProvider is an unsupported SPI.

Removal of the Map Store

The Map Store has been an experimental feature in previous releases. Starting with this release, it is removed and users should continue to use the current JPA store.

Since this release, it is no longer possible to use --storage related CLI options. The modules keycloak-model-map* have been removed.

Removed namespaces from our translations

All translations are moved into one file for the Admin Console. If you have made your own translations or extended the Admin Console you will need to migrate them to this new format. Also if you have "overrides" in your database, you will have to remove the namespace from the keys. Some keys are the same only in different namespaces, this is most obvious to help. In these cases we have postfix the key with Help.

If you want you can use this node script to help with the migration. It will take all the single files and put them into a new one and also take care of some of the mapping:

import { readFileSync, writeFileSync, appendFileSync } from "node:fs";

const ns = [

const map = new Map();
const dup = [];

ns.forEach((n) => {
  const rawData = readFileSync(n + ".json");
  const translation = JSON.parse(rawData);
  Object.entries(translation).map((e) => {
    const name = e[0];
    const value = e[1];
    if (map.has(name) && map.get(name) !== value) {
      if (n.includes("help")) {
        map.set(name + "Help", value);
      } else {
        map.set(name, value);
          name: name,
          value: map.get(name),
          dup: { ns: n, value: value },
    } else {
      map.set(name, value);

  JSON.stringify(Object.fromEntries(map.entries()), undefined, 2),

const mapping = [
  ["common:clientScope", "clientScopeType"],
  ["identity-providers:createSuccess", "createIdentityProviderSuccess"],
  ["identity-providers:createError", "createIdentityProviderError"],
  ["clients:createError", "createClientError"],
  ["clients:createSuccess", "createClientSuccess"],
  ["user-federation:createSuccess", "createUserProviderSuccess"],
  ["user-federation:createError", "createUserProviderError"],
  ["authentication-help:name", "flowNameHelp"],
  ["authentication-help:description", "flowDescriptionHelp"],
  ["clientScopes:noRoles", "noRoles-clientScope"],
  ["clientScopes:noRolesInstructions", "noRolesInstructions-clientScope"],
  ["users:noRoles", "noRoles-user"],
  ["users:noRolesInstructions", "noRolesInstructions-user"],
  ["clients:noRoles", "noRoles-client"],
  ["clients:noRolesInstructions", "noRolesInstructions-client"],
  ["groups:noRoles", "noRoles-group"],
  ["groups:noRolesInstructions", "noRolesInstructions-group"],
  ["roles:noRoles", "noRoles-roles"],
  ["roles:noRolesInstructions", "noRolesInstructions-roles"],
  ["realm:realmName:", "realmNameField"],
  ["client-scopes:searchFor", "searchForClientScope"],
  ["roles:searchFor", "searchForRoles"],
  ["authentication:title", "titleAuthentication"],
  ["events:title", "titleEvents"],
  ["roles:title", "titleRoles"],
  ["users:title", "titleUsers"],
  ["sessions:title", "titleSessions"],
  ["client-scopes:deleteConfirm", "deleteConfirmClientScopes"],
  ["users:deleteConfirm", "deleteConfirmUsers"],
  ["groups:deleteConfirm_one", "deleteConfirmGroup_one"],
  ["groups:deleteConfirm_other", "deleteConfirmGroup_other"],
  ["identity-providers:deleteConfirm", "deleteConfirmIdentityProvider"],
  ["realm-settings:deleteConfirm", "deleteConfirmRealmSetting"],
  ["roles:whoWillAppearLinkText", "whoWillAppearLinkTextRoles"],
  ["users:whoWillAppearLinkText", "whoWillAppearLinkTextUsers"],
  ["roles:whoWillAppearPopoverText", "whoWillAppearPopoverTextRoles"],
  ["users:whoWillAppearPopoverText", "whoWillAppearPopoverTextUsers"],
  ["client-scopes:deletedSuccess", "deletedSuccessClientScope"],
  ["identity-providers:deletedSuccess", "deletedSuccessIdentityProvider"],
  ["realm-settings:deleteSuccess", "deletedSuccessRealmSetting"],
  ["client-scopes:deleteError", "deletedErrorClientScope"],
  ["identity-providers:deleteError", "deletedErrorIdentityProvider"],
  ["realm-settings:deleteError", "deletedErrorRealmSetting"],
  ["realm-settings:saveSuccess", "realmSaveSuccess"],
  ["user-federation:saveSuccess", "userProviderSaveSuccess"],
  ["realm-settings:saveError", "realmSaveError"],
  ["user-federation:saveError", "userProviderSaveError"],
  ["realm-settings:validateName", "validateAttributeName"],
  ["identity-providers:disableConfirm", "disableConfirmIdentityProvider"],
  ["realm-settings:disableConfirm", "disableConfirmRealm"],
  ["client-scopes:updateSuccess", "updateSuccessClientScope"],
  ["client-scopes:updateError", "updateErrorClientScope"],
  ["identity-providers:updateSuccess", "updateSuccessIdentityProvider"],
  ["identity-providers:updateError", "updateErrorIdentityProvider"],
  ["user-federation:orderChangeSuccess", "orderChangeSuccessUserFed"],
  ["user-federation:orderChangeError", "orderChangeErrorUserFed"],
  ["authentication-help:alias", "authenticationAliasHelp"],
  ["authentication-help:flowType", "authenticationFlowTypeHelp"],
  ["authentication:createFlow", "authenticationCreateFlowHelp"],
  ["client-scopes-help:rolesScope", "clientScopesRolesScope"],
  ["client-scopes-help:name", "scopeNameHelp"],
  ["client-scopes-help:description", "scopeDescriptionHelp"],
  ["client-scopes-help:type", "scopeTypeHelp"],
  ["clients-help:description", "clientDescriptionHelp"],
  ["clients-help:clientType", "clientsClientTypeHelp"],
  ["clients-help:scopes", "clientsClientScopesHelp"],
  ["common:clientScope", "clientScopeTypes"],
  ["dashboard:realmName", "realmNameTitle"],
  ["common:description", "description"],

mapping.forEach((m) => {
  const key = m[0].split(":");
  try {
    const data = readFileSync(key[0] + ".json");
    const translation = JSON.parse(data);
    const value = translation[key[1]];
    if (value) {
        '"' + m[1] + '": ' + JSON.stringify(value) + ',\n',
  } catch (error) {
    console.error("skipping namespace key: " + key);

Save this into a file called transform.mjs in your public/locale/<language> folder and run it with:

node ./transform.mjs
This might not do a complete transform, but very close to it.

Migrating to 22.0.4

A new parameter for specify max length of email local part

A new parameter --spi-user-profile-declarative-user-profile-max-email-local-part-length is added to set max email local part length taking backwards compatibility into consideration. The default value is 64. Example of usage:

kc.[sh|bat] start --spi-user-profile-declarative-user-profile-max-email-local-part-length=100 ...

Migrating to 22.0.2

Never expires option removed from client advanced settings combos

The option Never expires is now removed from all the combos of the Advanced Settings client tab. This option was misleading because the different lifespans or idle timeouts were never infinite, but limited by the general user session or realm values. Therefore, this option is removed in favor of the other two remaining options: Inherits from the realm settings (the client uses general realm timeouts) and Expires in (the value is overriden for the client). Internally the Never expires was represented by -1. Now that value is shown with a warning in the Admin Console and cannot be set directly by the administrator.

New LinkedIn OpenID Connect social provider

A new social identity provider called LinkedIn OpenID Connect has been introduced for the business and employment-focused platform. LinkedIn released recently a new product for developers called Sign In with LinkedIn using OpenID Connect. The product provides a new way to authenticate members using OpenID Connect, but the default OpenID Connect v1.0 identity provider does not work with it at present time. For that reason, Keycloak adds this new identity provider as the specific social provider for the new product.

The old LinkedIn way based on OAuth seems to be completely removed from the developer portal. How the existing LinkedIn social provider is working with current applications is not clear. Keycloak maintains the old provider renamed to LinkedIn (deprecated), but in a deprecated feature called linkedin-oauth which is disabled by default. It will be removed in future versions. Please enable it again at startup if needed:

kc.[sh|bat] start --features linkedin-oauth ...

Migrating to 22.0.0

Transition from Java EE to Jakarta EE

Keycloak migrated its codebase from Java EE (Enterprise Edition) to its successor Jakarta EE, which brings various changes into Keycloak.

We have upgraded all Jakarta EE specifications in order to support Jakarta EE 10, such as:

  • Jakarta Persistence 3.1

  • Jakarta RESTful Web Services 3.1

  • Jakarta Mail API 2.1

  • Jakarta Servlet 6.0

  • Jakarta Activation 2.1

Jakarta EE 10 provides a modernized, simplified, lightweight approach to building cloud-native Java applications. The main changes provided within this initiative are changing the namespace from javax.* to jakarta.*. It does not apply for javax.* packages provided directly in the JDK, such as javax.security, javax.net, javax.crypto, etc.

You can be affected by these changes in your custom extensions, providers or JPA entities.

Upgrade to Quarkus 3

Keycloak upgraded to version 3 of the Quarkus Java framework. Quarkus 3 continues the tradition of propelling Java development by moving fast and providing a cutting-edge user experience with the latest technologies. It continues to improve overall performance and efficiency.

Quarkus 3 is based on Jakarta EE 10, the same as Keycloak, creating smooth interoperability between them. In addition, it contains Eclipse MicroProfile 6, which aligns with Jakarta EE 10 Core Profile. The central part of the Quarkus 3 upgrade is built-in support for JPA 3.1 and Hibernate ORM 6.

quarkus.hibernate-orm.* properties no longer working

For Quarkus 3, Hibernate ORM configurations must be specified in either the persistence.xml file or in Quarkus properties, but not in both places. Keycloak uses a persistence.xml file, therefore, it is no longer possible to override Keycloak’s JPA store configurations via Quarkus’ configuration properties for the default persistence unit whose names start with quarkus.hibernate-orm.

Upgrade to Hibernate ORM 6

Keycloak now benefits from the upgrade to Hibernate ORM 6.2, which includes improved performance, better SQL, modern JDK support, and support for modern RDBMS features. The performance improvements primarily affect JDBC, HQL Translation, and Criteria Translation.

If you have custom providers or JPA entities, these changes may affect you.

We recommend reviewing the Quarkus migration guide or the Hibernate release notes for more information.

Legacy Promise API removed from Keycloak JS adapter

The legacy Promise API methods have been removed from the Keycloak JS adapter. This means that calling .success() and .error() on promises returned from the adapter is no longer possible. Instead standardized Promise methods such as .then() and .catch() should be used.

Before migration:
const keycloak = new Keycloak();

  .success(function(authenticated) {
    alert(authenticated ? 'authenticated' : 'not authenticated');
  }).error(function() {
    alert('failed to initialize');
After migration:
const keycloak = new Keycloak();

  .then(function(authenticated) {
    alert(authenticated ? 'authenticated' : 'not authenticated');
  }).catch(function() {
    alert('failed to initialize');
Or alternatively, when using the await keyword to unwrap these promises:
const keycloak = new Keycloak();

try {
  const authenticated = await keycloak.init();
  alert(authenticated ? 'authenticated' : 'not authenticated');
} catch (error) {
  alert('failed to initialize');

Export and Import perform an automatic build

In previous releases, the export and import commands required a build command to be run first. Starting with this release, the export and import commands perform an automatic rebuild of Keycloak if a build time configuration has changed.

When migrating existing scripts that run a build command first, migrate by adding the --optimized command line option to the export and import command to avoid Keycloak automatically re-building the image. Not adding the --optimized option in this might make Keycloak trigger a rebuild and revert to the default values, and then connecting to the database for export and import will not work.

The following examples assume that runtime parameters like a database password are provided via a configuration file or an environment variable.

Before migration: Running the build command before running the export command
bin/kc.[sh|bat] build --db=postgres ...
bin/kc.[sh|bat] export --dir <dir>
After migration: Adding --optimized to the export command
bin/kc.[sh|bat] build --db=postgres ...
bin/kc.[sh|bat] export --optimized --dir <dir>
After migration: Leveraging the auto-build functionality
bin/kc.[sh|bat] export --dir <dir> --db=postgres ...

When the auto-build runs, the build time options will be in effect for all subsequent commands that are started with the --optimized flag, including the start command.

In previous releases the export and import commands allowed runtime parameters such as a database URL only in configuration files or environment variables. Starting with this release, those runtime parameters are now available on the command line as well. Use the --help option to find out about the supported parameters.

Renamed Keycloak Admin client artifacts

After the upgrade to Jakarta EE, artifacts for Keycloak Admin clients were renamed to more descriptive names with consideration for long-term maintainability. We still provide two separate Keycloak Admin clients, one with Jakarta EE and the other with Java EE support.

We stopped releasing the org.keycloak:keycloak-admin-client-jakarta artifact. The default one for the Keycloak Admin client with Jakarta EE support is org.keycloak:keycloak-admin-client (since version 22.0.0).

The new artifact with Java EE support is org.keycloak:keycloak-admin-client-jee.

Jakarta EE support
Before migration:
After migration:
Java EE support
Before migration:
After migration:

Passthrough proxy mode changes

Keycloak’s proxy configuration setting for mode passthrough no longer parses HTTP forwarding headers in the request, as when a proxy forwards an HTTPS connection in passthrough mode, a proxy is unable to add, remove or update HTTP headers.

Installations that want the HTTP headers in the client’s request to be parsed should use the edge or reencrypt setting.

See Using a reverse proxy for details.

Consistent fallback message resolving for all themes

This change only may affect you when you are using realm localization messages.

Up to this version, the resolving of fallback messages was inconsistent across themes, when realm localization messages were used. More information can be found in the following issue.

The implementation has now been unified for all themes. In general, the message for the most specific matching language tag has the highest priority. If there are both a realm localization message and a Theme 18n message, the realm localization message has the higher priority. Summarized, the priority of the messages is as follows (RL = realm localization, T = Theme i18n files): RL <variant> > T <variant> > RL <region> > T <region> > RL <language> > T <language> > RL en > T en.

Probably this can be better explained with an example: When the variant de-CH-1996 is requested and there is a realm localization message for the variant, this message will be used. If such a realm localization message does not exist, the Theme i18n files are searched for a corresponding message for that variant. If such a message does not exist, a realm localization message for the region (de-CH) will be searched. If such a realm localization message does not exist, the Theme i18n files are searched for a message for that region. If still no message is found, a realm localization message for the language (de) will be searched. If there is no matching realm localization message, the Theme i18n files are be searched for a message for that language. As last fallback, the English (en) translation is used: First, an English realm localization will be searched - if not found, the Theme 18n files are searched for an English message.

UserQueryProvider changes

UserQueryProvider interface was split into two. One is UserQueryMethodsProvider providing capabilities for querying users. Second one is UserCountMethodsProvider which provides capability for counting number of users in particular storage.

Keycloak now has the ability to differentiate between user storage providers that can efficiently execute count queries and those that cannot. The UserQueryProvider interface still exists and extends both new interfaces. Therefore, there is no need for any modifications in the existing implementations of UserQueryProvider since it retains the same methods.

LDAPStorageProvider search changes

Starting with this release Keycloak uses a pagination mechanism when querying federated LDAP database. Searching for users should be consistent with search in local database.

Since this release LDAPStorageProvider implements only UserQueryMethodsProvider, not UserQueryProvider.

Deprecation of Keycloak OpenID Connect Adapters

Starting with this release, we no longer will invest our time on the following Keycloak OpenID Connect Adapters:

  • Keycloak Wildfly OpenID Connect Adapter

  • Keycloak JEE Servlet OpenID Connect Adapter

  • Keycloak Spring Boot and Spring Security OpenID Connect Adapter

This move is already reflected in our documentation and in our quickstart repository. Please, consider looking at the following references for more information:

We recommend starting to look into moving your applications to the alternatives from the references above. Those adapters should not be available anymore in future releases.

Deprecation of Keycloak JEE SAML Adapter

The Keycloak JEE SAML Adapter has been discontinued, and we will no longer invest our time on its development following this release.

The official adapter is now based on Jakarta and should be used as soon as you switch your applications to this technology.

This change is already in our documentation and in our quickstart repository. For more information, please consider looking at the following references:

If you cannot migrate your applications to Jakarta, you can still use the "legacy" SAML JEE adapter and still be able to integrate with future releases of the server. However, consider upgrading your applications as soon as possible because we are no longer providing support to JEE.

Changes for openshift-integration feature

The preview feature openshift-integration was removed from Keycloak codebase and moved into separate extension. This includes moving of related providers such as custom client storage provider and token review endpoint for Openshift integration.

If you used this feature, you should not use the openshift-integration feature anymore when starting Keycloak server and instead you need to deploy the JAR file from custom extension. You can check the Openshift extension and the instructions in it’s README file for how to deploy the extension to your Keycloak server.

The Openshift extension is not officially supported and maintained by Keycloak team. You can use it only at your own risk.
Http Challenge flow removed

The built-in authentication flow http challenge was removed along with the authenticator implementations no-cookie-redirect, basic-auth, and basic-auth-otp. The http challenge authentication flow was also intended for Openshift integration and therefore it was removed along with other related capabilities as described above. Authenticator implementations were moved to the Openshift extension described in the previous paragraph.

If you use the http challenge flow as a realm flow or as First Broker Login or Post Broker Login flow for any of your identity providers, the migration is not possible. Be sure to update your realm configuration to eliminate the use of the http challenge flow before migration. If you use the http challenge flow as Authentication Flow Binding Override for any client, the migration would complete, but you could no longer log in to that client. After the migration, you would need to re-create the flow and update the configuration of your clients to use the new/differentJson flow.

Removing thirdparty dependencies

The removal of openshift-integration allows us to remove few thirdparty dependencies from Keycloak distribution. This includes openshift-rest-client, okio-jvm, okhttp, commons-lang, commons-compress, jboss-dmr and kotlin-stdlib. This means that if you use any of these libraries as dependencies of your own providers deployed to Keycloak server, you may also need to copy those jar files explicitly to the Keycloak distribution providers directory as well.

Context and dependency injection no longer enabled to JAX-RS Resources

In order to provide a better runtime and leverage as much as possible the underlying stack, all injection points for contextual data using the javax.ws.rs.core.Context annotation were removed. The expected improvement in performance involves no longer creating proxies instances multiple times during the request lifecycle, and drastically reducing the amount of reflection code at runtime.

If you are extending one of the following SPIs:

  • PolicySpi

  • AdminRealmResourceSpi

  • IdentityProviderSpi

  • RealmResourceSPI

You should review your custom JAX-RS (sub)resources in order to obtain any contextual data as follows:

KeycloakSession session = org.keycloak.common.util.Resteasy.getContextData(KeycloakSession.class);

If you need access to the current request and response objects, you can now obtain their instances directly from the KeycloakSession:

org.jboss.resteasy.spi.HttpRequest request;
org.jboss.resteasy.spi.HttpResponse response;

was replaced by:

KeycloakSession session = // obtain the session, which is usually available when creating a custom provider from a factory
KeycloakContext context = session.getContext();

HttpRequest request = context.getHttpRequest();
HttpResponse response = context.getHttpResponse();

In case you have no access to a KeycloakSession instance when invoking a JAX-RS resource method, you can obtain contextual data from the JAX-RS runtime as follows:

KeycloakSession session = org.keycloak.common.util.Resteasy.getContextData(KeycloakSession.class);

Additional contextual data can be obtained from the runtime through the KeycloakContext instance:

KeycloakSession session = // obtain the session
KeycloakContext context = session.getContext();
MyContextualObject myContextualObject = context.getContextObject(MyContextualObject.class);

Upgrading your custom JAX-RS resources

If you are extending the server’s REST APIs through the following SPIs:

  • PolicySpi

  • AdminRealmResourceSpi

  • IdentityProviderSpi

  • RealmResourceSPI

You need to add an empty META-INF/beans.xml to the JAR file where your custom providers are packaged. Otherwise, they are not recognized by the server at runtime.

If you are using RealmResourceSPI or AdminRealmResourceSpi, you have the choice between adding an empty file named beans.xml under META-INF or annotating the JAX-RS resource classes with the jakarta.ws.rs.ext.Provider annotation.

You should also make sure your JAX-RS methods are declaring the expected media types for input and output by marking them with the @Consumes and @Produces annotations, respectively.

Deprecated methods from data providers and models

In earlier versions of Keycloak, provider and model interfaces underwent a cleanup process that involved deprecating certain methods. In this release the methods were removed and some additional methods were deprecated. The Javadoc for these methods from Keycloak 21 included information about their corresponding replacements.

  • RealmModel#searchForGroupByNameStream(String, Integer, Integer) was removed.

  • UserProvider#getUsersStream(RealmModel, boolean) was removed.

  • UserSessionPersisterProvider#loadUserSessions(int, int, boolean, int, String) was removed.

  • Interfaces added for Streamification work were removed. Such as RoleMapperModel.Streams and similar.

  • Streams interfaces in federated storage provider classes were deprecated.

  • KeycloakModelUtils#getClientScopeMappings was removed.

  • Deprecated methods from KeycloakSession were removed.

  • UserQueryProvider#getUsersStream methods were removed.

Multiple Keycloak instances

Multiple Keycloak CRs may be created in the same namespace and will be managed independently by the operator. To allow for this StatefulSets created by older versions of the operator must be re-created. This will happen automatically when the operator is upgraded and lead to small amount of downtime.

k8s.keycloak.org/v2alpha1 changes

The condition status field was changed from a boolean to a string for conformance with standard Kubernetes conditions. In the CRD it will temporarily be represented as accepting any content, but it will only ever be a string. Please make sure any of your usage of this field is updated to expect the values "True", "False", or "Unknown", rather than true or false.

Keycloak supports IPv4/IPv6 dual stack

Keycloak supports the IPv4/IPv6 dual stack and can be accessible by default via the IPv4 and IPv6 addresses. In the older versions of Keycloak, the default approach was to use only IPv4 addresses.

Migrating to 21.1.0

Javascript engine available by default on the classpath

In the previous version, when Keycloak was used on Java 17 with Javascript providers (Script authenticator, Javascript authorization policy or Script protocol mappers for OIDC and SAML clients), it was needed to copy javascript engine to the distribution. This is no longer needed as Nashorn javascript engine is available in Keycloak server by default. When you deploy script providers, it is recommended to not copy the nashorn script engine and it’s dependencies into the Keycloak distribution.

Change of the default Client ID mapper of Service Account Client

Default Client ID mapper of Service Account Client has been changed. Token Claim Name field value has been changed from clientId to client_id. client_id claim is compliant with OAuth2 specifications:

clientId userSession note still exists.

Keycloak JS adapter must be instanciated with the new operator

Historically it has been possible to create an instance of the Keycloak JS adapter by calling the Keycloak() function directly:

const keycloak = Keycloak();

To align this with modern conventions in the JavaScript world it has been possible to use the new operator to create an instance instead:

const keycloak = new Keycloak();

The function-style constructor has been deprecated for a while, but starting this version we will actively log a deprecation message when it used. This style of constructor will be removed in a future version so make sure to migrate your code to use the new operator.

Migrating to 21.0.2

Terms and Conditions user attribute migration

The terms_and_conditions user attribute was accidentally changed in 21.0.0 to uppercase. This version reverts the user attribute back to lowercase. The value of the attribute is set when accepting Terms and Conditions page.

If any of your custom extensions relies on this attribute, you may need to adjust your code to check both attributes terms_and_conditions and TERMS_AND_CONDITIONS.

Migrating to 21.0.0

Keycloak uses Micrometer for metrics

Keycloak provides an optional a metrics endpoint which exports metrics in the Prometheus format. In this release the implementation to provide this data switched from SmallRye to Micrometer, which is the recommended metrics library for Quarkus.

Due to this change, metrics have been renamed. The following table shows some examples.

Before upgrading it is recommended to review all metrics returned from the endpoint before and after the change, and update their usage in dashboards and alerts.

Table 1. Examples of changed metrics names
Old metric name New metric name









Deprecated RSA_SHA1 and DSA_SHA1 algorithms for SAML

Algorithms RSA_SHA1 and DSA_SHA1, which can be configured as Signature algorithms on SAML adapters, clients and identity providers are deprecated. We recommend to use safer alternatives based on SHA256 or SHA512. Also, verifying signatures on signed SAML documents or assertions with these algorithms do not work on Java 17 or higher. If you use this algorithm and the other party consuming your SAML documents is running on Java 17 or higher, verifying signatures will not work.

The possible workaround is to remove algorithms such as http://www.w3.org/2000/09/xmldsig#rsa-sha1 or http://www.w3.org/2000/09/xmldsig#dsa-sha1 from the list of "disallowed algorithms" configured on property jdk.xml.dsig.secureValidationPolicy in the file $JAVA_HOME/conf/security/java.security.

SAML SP metadata changes

In this version, Keycloak will refuse to decrypt assertions encrypted using a realm key generated for signing purpose. This change means all encrypted communication from IDP to SP (where Keycloak acts as the SP) will stop working.

There are two ways to make this work:

  • either update the IDP configuration with the metadata generated by a newer version of Keycloak,

  • or run Keycloak in backward compatibility mode that will make Keycloak work with the metadata generated by older Keycloak versions. This mode can be enabled using -Dkeycloak.saml.deprecated.encryption=true flag. Note this backward compatibility mode is planned to be removed in Keycloak 24.

Deprecated methods from user session provider were removed

In Keycloak 13 there was introduced UserLoginFailureProvider and some methods from UserSessionProvider were moved there. The methods in UserSessionProvider were deprecated and now has been removed. Javadoc of these methods contained a corresponding replacement (see Javadoc of Keycloak 20 release).

Custom themes using old admin console won’t work

The old admin console, which was deprecated in previous versions, was finally removed. This also means that your custom themes, which were using it as parent theme or importing from it, won’t work. It is highly recommended to not deploy such themes at all as extending old admin console is not applicable anymore and there can be issues in Keycloak (at least warnings or errors in the logs) with such themes deployed.

Curl has been removed from the container

The Keycloak Container Image has been modified to enhance security. As a result, curl and other CLI tools have been removed, which you may have been using in your customized image. See the updated container guide for information on how to handle this change.

Migrating to 20.0.0

RESTEasy Version Update

Updated the RESTEasy version of Keycloak Admin REST Client to the next major version.

H2 Version Update

Keycloak ships for development purposes with an H2 database driver. As it is intended for development purposes only, it should never be used in a production environment.

In this release, the H2 driver has been upgraded from version 1.x to version 2.x. This change might require changes to the H2 JDBC URL or migration of the H2 database files in an existing Keycloak setup.

Changes to the H2 JDBC URL

For Keycloak to run with its JPA legacy store with H2 version 2.x, the JDBC URL requires the attribute NON_KEYWORDS=VALUE.

In a setup where H2 is initialized by Keycloak without extra parameters, Keycloak will append the attribute automatically. This is the default for development setups.

If an H2 JDBC URL is provided on the command line or in a configuration file, and it already contains the NON_KEYWORDS= attribute in the JDBC URL, this attribute needs to be amended with the VALUE keyword.

If the connection factory for the H2 database is initialized outside Keycloak, that initialization needs to take care of adding the NON_KEYWORDS attribute.

See the H2 documentation on the NON_KEYWORDS attribute for details.

Upgrade of H2 database files

H2 database base files created with H2 version 1.x should not be used with version 2.x.

Purge existing H2 database files to start with an empty database, export and import the realms using Keycloak’s export and import functionality, or refer to the migration notes on the H2 database project’s website for details on how to migrate H2 database contents.

Breaking changes in the new version of Keycloak Operator

In order to use the newest version of the Keycloak Operator, the manual reinstallation and upgrade of your CRs are required. There’s no automated migration.

This release contains the following breaking changes in Keycloak CRs:

serverConfiguration free-form field was renamed

From now on it’s called additionalOptions. The idea behind this decision is to align it more with the Keycloak Quarkus distribution and achieve/preserve a naming consistency. serverConfiguration can be still used for configuring options that haven’t got a declared alternative in Keycloak custom resources (CRs). A good example of such use can be service providers.

Ingress options were refined

In the past, it used to be defined via the disableDefaultIngress property. We decided to clarify it a bit, thus from now on you can use the following structure to control your ingress settings:

      enabled: false
HTTP options were added

Similarly, like with ingress, you can define multiple HTTP options in a better structural manner:

    httpEnabled: true
    httpPort: 80
    httpsPort: 443
    tlsSecret: my-tls-secret
Hostname options were added

Last but not least, the hostname options were also changed:

    hostname: [keycloak-server-hostname]
    admin: [admin-console-hostname]
    adminUrl: [admin-console-base-url]
    strict: [true|false]
    strictBackchannel: [true|false]
Some fields are no longer required

The hostname and tlsSecret fields are now optional to align with the Quarkus distribution configuration. With that we also removed the possibility to set INSECURE-DISABLE special value to those fields. In order to disable hostname checks and enable HTTP, please follow the same approach as with the Quarkus distribution, i.e. set strict: false, strictBackchannel: false and httpEnabled: true fields.

OLM channel was changed to fast

The default channel of Keycloak Operator Lifecycle Manager was changed to fast.

Deprecated methods from data providers and models were removed

Prior to Keycloak 15, there was a clean-up of provider and model interfaces where we deprecated some methods. Javadoc of these methods contained a corresponding replacement method (see Javadoc of Keycloak 19 release). In this release the methods were removed. The following is a list of all changed classes.

The most common patterns for deprecating and removing the methods are the following.

  • Streamification - interfaces now contain only Stream-based methods.

    For example in GroupProvider interface

    List<GroupModel> getGroups(RealmModel realm);

    was replaced by

    Stream<GroupModel> getGroupsStream(RealmModel realm);

    More details on streamification work can be found in KEYCLOAK-14011.

  • Consistent parameter ordering - methods now have strict parameter ordering where RealmModel is always the first parameter.

    For example in UserLookupProvider interface

    UserModel getUserById(String id, RealmModel realm);

    was replaced by

    UserModel getUserById(RealmModel realm, String id)
List of changed interfaces

(o.k. stands for org.keycloak. package)

  • server-spi module

    • o.k.credential.CredentialInputUpdater

    • o.k.credential.UserCredentialStore

    • o.k.models.ClientProvider

    • o.k.models.ClientSessionContext

    • o.k.models.GroupModel

    • o.k.models.GroupProvider

    • o.k.models.KeyManager

    • o.k.models.KeycloakSessionFactory

    • o.k.models.ProtocolMapperContainerModel

    • o.k.models.RealmModel

    • o.k.models.RealmProvider

    • o.k.models.RoleContainerModel

    • o.k.models.RoleMapperModel

    • o.k.models.RoleModel

    • o.k.models.RoleProvider

    • o.k.models.ScopeContainerModel

    • o.k.models.UserCredentialManager

    • o.k.models.UserModel

    • o.k.models.UserProvider

    • o.k.models.UserSessionProvider

    • o.k.models.utils.RoleUtils

    • o.k.sessions.AuthenticationSessionProvider

    • o.k.storage.client.ClientLookupProvider

    • o.k.storage.group.GroupLookupProvider

    • o.k.storage.user.UserLookupProvider

    • o.k.storage.user.UserQueryProvider

  • server-spi-private module

    • o.k.events.EventQuery

    • o.k.events.admin.AdminEventQuery

    • o.k.keys.KeyProvider

All changes are linked to the following issue.

Migrating to 19.0.2

OpenID Connect Logout Prompt

At Keycloak 18.0.0, the logout is now compatible with the new OIDC specification, which changed the handling for the url parameters. However, to also remain compatible with earlier versions, a compatibility flag is introduced. See the Upgrading Guide for further information for the backwards compatibility option, which allows your application to still use the old format for the url parameters.

While the url parameters can now be configured to be compatible, there was still one incompatibility with keycloak 17 and earlier releases. If the user does not provide a valid idTokenHint, a logout prompt appears instead of a successful logout redirect. Therefore, a new compatibility flag suppress-logout-confirmation-screen is introduced to suppress the logout screen.

You can enable this parameter when you start the server by entering the following command:

bin/kc.[sh|bat] --spi-login-protocol-openid-connect-suppress-logout-confirmation-screen=true start

With this configuration, you can still use the logout endpoint without a user prompt.

The backwards compatibility switch will be removed in some future version - probably Keycloak 23. You are encouraged to update your clients as soon as possible as described above rather than rely on this switch.

Deploying scripts through SAML javascript protocol mapper

Until now, administrators, which used SAML javascript protocol mapper on their SAML clients or client scopes, were allowed to upload scripts to the server through the Keycloak Administration Console as well as through the RESTful Admin API.

For now on, this capability is disabled and users should deploy scripts directly to the server. This behaviour is aligned with other script based providers. For more details, please take a look at JavaScript Providers.

UserInfo Endpoint Changes

Error response changes

The UserInfo endpoint is now returning error responses fully compliant with RFC 6750 (The OAuth 2.0 Authorization Framework: Bearer Token Usage). Error code and description (if available) are provided as WWW-Authenticate challenge attributes rather than JSON object fields. The responses will be the following, depending on the error condition:

  • In case no access token is provided:

    401 Unauthorized
    WWW-Authenticate: Bearer realm="myrealm"
  • In case several methods are used simultaneously to provide an access token (for example, Authorization header + POST access_token parameter), or POST parameters are duplicated:

    400 Bad Request
    WWW-Authenticate: Bearer realm="myrealm", error="invalid_request", error_description="..."
  • In case an access token is missing openid scope:

    403 Forbidden
    WWW-Authenticate: Bearer realm="myrealm", error="insufficient_scope", error_description="Missing openid scope"
  • In case of inability to resolve cryptographic keys for UserInfo response signing/encryption:

    500 Internal Server Error
  • In case of a token validation error, a 401 Unauthorized is returned in combination with the invalid_token error code. This error includes user and client related checks and actually captures all the remaining error cases:

    401 Unauthorized
    WWW-Authenticate: Bearer realm="myrealm", error="invalid_token", error_description="..."
Other Changes
  • It is now required for access tokens to have the openid scope, which is stipulated by UserInfo being a feature specific to OpenID Connect and not OAuth 2.0. If the openid scope is missing from the token, the request will be denied with a 403 Forbidden (see above).

  • UserInfo now checks the user status, and returns the invalid_token response if the user is disabled.

Migrating to 19.0.0

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


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


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


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.

This change impacts deployments of existing user storage providers in the Wildfly distribution. If your user storage provider is deployed as a WAR archive, you need to add the META-INF/jboss-deployment-structure.xml file into that archive stating the modified dependencies as shown below:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
            <module name="org.keycloak.keycloak-model-legacy" meta-inf="import"/>
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 situation 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
After migration: accessing the new API caller does not depend on the legacy storage API
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
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
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 transparent 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

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) {
            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) {
            public String getUsername() {
                return username;

            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
    app: keycloak
  name: keycloak
  maxUnavailable: 1
      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.

Migrating to 18.0.0

Step-up authentication

Step-up authentication is a new feature. This feature provides the acr client scope, which contains a protocol mapper that is supposed to add the acr claim in the token. The acr claim is not added automatically now as it was before this version, but it is added with the usage of this client scope and protocol mapper.

The client scope is added as a realm "default" client scope and hence will be added to all newly created clients. For performance reasons, the client scope is not automatically added to all existing clients during migration. The clients will not have an acr claim by default after the migration. Consider these possible actions:

  • If you do not plan to use step-up authentication feature, but you rely on the acr claim in the token, you can disable step_up_authentication feature. The claim will be added with the value 1 in case of normal authentication and 0 in case of SSO authentication.

  • Add acr client scope to your clients manually by admin REST API or admin console. This is needed especially if you want to use step-up authentication. If you have a large number of clients in the realm and want to use acr claim for all of them, you can trigger some SQL similar to this against your DB. However, remember to clear the cache or restart the server if Keycloak is already started:


OpenID Connect Logout

Previous versions of Keycloak had supported automatic logout of the user and redirecting to the application by opening logout endpoint URL such as http(s)://example-host/auth/realms/my-realm-name/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri. While that implementation was easy to use, it had potentially negative impact on performance and security. The new version has better support for logout based on the OpenID Connect RP-Initiated Logout specification. The parameter redirect_uri is no longer supported; also, in the new version, the user needs to confirm the logout. It is possible to omit the confirmation and do automatic redirect to the application when you include parameter post_logout_redirect_uri together with the parameter id_token_hint with the ID Token used for login.

The existing deployments are affected in the following ways:

  • If your application directly uses links to logout endpoint with the redirect_uri parameter, you may be required to change this as described above. Consider either removing the redirect_uri parameter entirely or replacing it with the id_token_hint and post_logout_redirect_uri parameters.

  • If you use java adapters and your application does logout by call httpServletRequest.logout(), you are not affected because this call uses the backchannel variant of the logout endpoint and that one was not changed.

  • If you use the latest javascript adapter, you are also not affected. However if your application uses an older version of the JavaScript adapter, you are affected as this adapter uses the variant of the logout endpoint with the deprecated redirect_uri parameter. In this case, you may need to upgrade to the latest version of the JavaScript adapter.

  • For the Node.js adapter, the same guideline applies as for the JavaScript adapter. You are encouraged to update to the latest version as the older version of the adapter uses the deprecated redirect_uri parameter. With the latest Node.js adapter, you are not affected as long as you use the logout based on the /logout URL as described in the documentation or in the Node.js adapter example. However, in the case when your application directly uses the method keycloak.logoutUrl, you can consider adding idTokenHint as the second argument to this method. The possibility to add idTokenHint as second argument was newly added in this version. The idTokenHint needs to be a valid ID Token that was obtained during the login. Adding idTokenHint is optional, but if you omit it, your users will need to confirm the logout screen as described earlier. Also they will not be redirected back to the application after logout.

There is a backwards compatibility option, which allows your application to still use the old format of the redirect_uri parameter.

You can enable this parameter when you start the server by entering the following command:

bin/kc.[sh|bat] --spi-login-protocol-openid-connect-legacy-logout-redirect-uri=true start

With this configuration, you can still use the format with the redirect_uri parameter. Note the confirmation screen will be needed if the id_token_hint is omitted.

The backwards compatibility switch will be removed in some future version - probably Keycloak 23. You are encouraged to update your clients as soon as possible as described above rather than rely on this switch.

Removal of the upload-scripts feature

Previous versions of Keycloak had supported managing JavaScript code through the management interfaces like the administrations console and REST API. Starting from this version this is no longer possible, and you should now deploy your scripts to the server in order to configure the following providers:

  • OpenID Connect Script Mapper

  • Script Authenticator (Authentication Execution)

  • JavaScript Policies

More details about how to deploy scripts to the server are available in the documentation. Note that to use scripts, you are still required to enable the scripts technology preview feature.

kc.[sh|bat] start --auto-build --features=preview

When deploying scripts, the server is going to automatically create their corresponding providers so that you can select them when configuring authentication flows, mappers, and authorization policies.

In general, the steps to update your realms are the following:

  • Before upgrading, remove any script provider you are using.

  • After the upgrade, deploy your scripts following the instructions in the documentation.

  • Update your authentication flows, mappers, and the client authorization settings to use the providers created from the scripts deployed to the server.

Account console Patternfly upgrade

The Patternfly (PF) React libraries have been updated, @patternfly/react-core from v3.153.3 to v4.147.0, @patternfly/react-icons from v3.15.16 to v 4.11.8, and @patternfly/react-styles from v3.7.14 to v4.11.8. Several minor UI updates were made to bring the account console into alignment with PF design standards.

Custom developed account UIs might not be compatible with these updates due to the breaking changes in PF. Most breaking changes should be resolvable by updating props on PF components.


Components known to have breaking changes:

  • Alert

  • action prop changed to actionClose

  • Expandable

  • renamed to ExpandableSection

  • Title

  • size attr now uses TitleSizes

  • DataListContent

  • noPadding changed to hasNoPadding

  • Grid, Stack, Level, Gallery

  • gutter attr changed to hasGutter

  • Modal

  • sizing control changed from, e.g. isLarge, to use ModalVariant, e.g. variant={ModalVariant.large}

  • Select

  • ariaLabelTypeAhead to typeAheadAriaLabel

  • isExpanded to isOpen

  • ariaLabelledBy to aria-labelledby

  • DataListContent

  • noPadding to hasNoPadding

Quarkus distribution: Split metrics-enabled option into health-enabled and metrics-enabled

The metrics-enabled option now only enables the metrics for Keycloak. To enable the readiness and liveness health endpoints, there’s a new boolean option health-enabled. This allows more fine-grained usage of these options, e.g. enabling metrics but not enabling readiness/liveness probes for on-premise use cases. In order to keep the same behaviour as before when metrics-enabled=true was set, you need to additionally set health-enabled=true when building Keycloak.

Migrating to 17.0.0

Default distribution is now powered by Quarkus

The default distribution of Keycloak is now powered by Quarkus, which brings a number of breaking changes to you configure Keycloak and deploy custom providers. For more information check out the Quarkus Migration Guide.

The WildFly distribution of Keycloak is now deprecated, with support ending June 2022. We recommend migrating to the Quarkus distribution as soon as possible. However, if you need to remain on the legacy WildFly distribution for some time, there are some changes to consider:

  • Container images for the legacy distribution tags have changed. To use the legacy distribution use the tags legacy or 17.0.0-legacy.

  • Download on the website for the legacy distribution has changed to keycloak-legacy-17.0.0.[zip|tar.gz].

If you encounter problems migrating to the Quarkus distribution, missing ability to configure something, or have general ideas and feedback, please open a discussion in GitHub Discussions.

Migrating from the preview Quarkus distribution

A number of things have changed since the preview Quarkus distribution was released in Keycloak 15.1.0. The ideal way to learn about what’s changed is to check out the new Server guides. In summary, the changes include:

  • Container now published to quay.io/keycloak/keycloak:latest and quay.io/keycloak/keycloak:17.0.0

  • Download on website renamed to keycloak-17.0.0.[zip|tar.gz].

  • conf/keycloak.properties changed to conf/keycloak.conf, which unifies configuration keys between the config file and CLI arguments.

  • Clearer separation between build options and runtime configuration.

  • Custom Quarkus configuration is done through conf/quarkus.properties.

  • h2-mem and h2-file databases renamed to dev-mem and dev-file.

  • Features are now enabled/disabled with --features and --features-disabled replacing the previous approach that had a separate config key for each feature.

  • Runtime configuration can no longer be passed to kc.[sh|bat] build and is no longer persisted in the build

  • Logging level and format is now configured with --log-level and --log-format, while in the past these had to be configured using unsupported Quarkus properties.

Client Policies Migration : client-scopes

If you used a policy including client-scopes condition and edited JSON document directly, you will need to change the "scope" field name in a JSON document to "scopes".

Liquibase upgraded to version 4.6.2

Liquibase was updated from version 3.5.5 to 4.6.2, which includes, among other things, several bug fixes, and a new way of registering custom extensions using ServiceLoader.

Migration from previous Keycloak versions to Keycloak 17.0.0 has been extensively tested with all currently supported databases, but we would like to stress the importance of closely following the Upgrading Guide, specifically of backing up existing database before upgrade. While we did our best to test the consequences of the Liquibase upgrade, some installations could be using specific setup unknown to us.

Migrating to 16.0.0

WildFly 25 upgrade

WildFly 25 deprecates the legacy security subsystem that among other things was used to configure TLS. Due to the amount of changes we are not able to provide migration scripts as we have done in the past.

We recommend that rather than copying configuration files from previous versions of Keycloak that you start with the default configuration files provided in Keycloak 16 and apply the relevant changes.

Configuration for the Keycloak subsystem can be copied directly.

For more information around the Elytron subsystem refer to the WildFly documentation.

We are really sorry for this inconvenience and understand this will make it significantly harder for everyone to upgrade to Keycloak 16, but we simply have not been able to find an alternative approach.

One thing worth pointing out is the switch to Quarkus distribution, which we plan to make fully supported in Keycloak 17, will make it significantly easier to configure and upgrade Keycloak.

For more information on WildFly 25 refer to the WildFly 25 release notes.

Proxy environment variables

Keycloak now respects the standard HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables for outgoing HTTP requests. This change could lead to unexpected use of a proxy server if you have for example the HTTP_PROXY variable defined but have no explicit proxy mappings specified in your SPI configuration. To prevent Keycloak from using those environment variables, you can explicitly create a no proxy route for all requests as .*;NO_PROXY.

Deprecated features in the Keycloak Operator

With this release, we have deprecated and/or marked as unsupported some features in the Keycloak Operator. This concerns the Backup CRD and the operator managed Postgres Database.

Keycloak Operator examples including unsupported Metrics extension

Previously, an unsupported metrics extension was added in the example for the creation of the Keycloak CR by the Keycloak Operator. This has been removed.

Migrating to 14.0.0

Client policies migration

The Client policies feature was a preview feature since Keycloak 12 and did not have proper support. If you tried this feature and configured some client policies or client profiles in Keycloak 12 or Keycloak 13, then you will need to configure your client policies and client profiles again in the new version. The format of the configuration changed significantly as it was only a preview and hence we do not provide automatic migration of client policies and client profiles created in Keycloak 12 or Keycloak 13.

Migrating to 13.0.0

Manual migration step needed

Manual change is required when the standalone.xml contains references to any of the SmallRye modules. SmallRye modules were removed from the underlying WildFly distribution, and the server does not start if the configuration references them. Thus if the configuration in the standalone.xml refers to these modules, the server configuration migration via migrate-standalone.cli fails before any changes are made to the configuration. In such case, to perform server configuration migration, you have to manually remove all the lines that refer to SmallRye modules. In the default configuration, you need to remove specifically the following lines:

<extension module="org.wildfly.extension.microprofile.config-smallrye"/>
<extension module="org.wildfly.extension.microprofile.health-smallrye"/>
<extension module="org.wildfly.extension.microprofile.metrics-smallrye"/>
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"/>
<subsystem xmlns="urn:wildfly:microprofile-health-smallrye:2.0" security-enabled="false" empty-liveness-checks-status="${env.MP_HEALTH_EMPTY_LIVENESS_CHECKS_STATUS:UP}" empty-readiness-checks-status="${env.MP_HEALTH_EMPTY_READINESS_CHECKS_STATUS:UP}"/>
<subsystem xmlns="urn:wildfly:microprofile-metrics-smallrye:2.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:wildfly}"/>

Upgrade to Wildfly 23

The Keycloak server was upgraded to use Wildfly 23 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 23 server. For example, Infinispan is now 11.0.9.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically. However, here are the most important changes, which you may need if you made your own configuration changes:

  • The module attribute of Infinispan cache containers is now deprecated (unused) and is replaced with the modules attribute, representing the set of modules associated with this cache container’s configuration. Moreover, there were also additional changes to attributes of various elements, originating from the use of Wildfly 23 as the underlying container. For example, the managed-executor-service and managed-scheduled-executor-service elements now recognize the new hung-task-termination-period attribute. See the Wildfly 23 full model reference for details.

Upgrade to Wildfly 22

The Keycloak server was upgraded to use Wildfly 22 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 22 server. For example, Infinispan is now 11.0.8.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically. However, here are the most important changes, which you may need if you made your own configuration changes:

Migrating to 12.0.2

Read-only attributes

There was added support for the read-only user attributes. This includes the user attributes, which are not supposed to be edited by the user or by the administrator when updating user with REST API or with the Keycloak user interfaces. It can be important especially if you use:

  • Custom user storage providers

  • Custom authenticators

  • Custom JavaScript authorization policies, which establish authorization based on some user attribute

  • X.509 authenticator with custom attribute for mapping the X.509 certificate to the user identity

  • Any other custom functionality, where some of the user attributes are used as the metadata for storing authentication/authorization/identity context rather than simple user profile information.

See the details in the Threat model mitigation chapter.

Valid Request URIs

If you use the OpenID Connect parameter request_uri, a requirement exists that your client needs to have Valid Request URIs configured. This can be configured through the admin console on the client details page or through the admin REST API or client registration API. Valid Request URIs need to contain the list of Request URI values, which are permitted for the particular client. This is to avoid SSRF attacks. There is possibility to use wildcards or relative paths similarly such as the Valid Redirect URIs option, however for security purposes, we typically recommend to use as specific value as possible.

Migrating to 13.0.0

Upgrade to Wildfly 22

The Keycloak server was upgraded to use Wildfly 22 as the underlying container. This does not directly involve any specific Keycloak server functionality, but a few changes related to the migration, which are worth mentioning.

Dependency updates

The dependencies were updated to the versions used by Wildfly 22 server. For example, Infinispan is now 11.0.8.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically. If more detail is needed, because, for example, you did some configuration changes on your own, the list of the most important changes follows:

Migrating to 12.0.0

Upgrade to Wildfly 21

The Keycloak server was upgraded to use Wildfly 21 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 21 server. For example, Infinispan is now 11.0.4.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically. However, here are the most important changes, which you may need if you made your own configuration changes:

  • The object-memory element of Infinispan caches is now deprecated (unused) and is replaced with the heap-memory element.

Skip creation of user session for the Docker protocol authentication

No user session is created after successful authentication with the Docker protocol. For details, please refer to the Server Administration Guide.

Upgrade to PatternFly 4

The Keycloak login theme components have been upgraded to PatternFly 4. The old PatternFly 3 runs simultaneously with the new one, so it’s possible to keep PF3 components. However, some changes to the design of the login theme were performed. Please upgrade your custom login theme due them. An example with the necessary changes can be found in the keycloak/examples/themes/theme/sunrise directory. No additional setup is required.

Client Credentials Grant without refresh token by default

From this Keycloak version, the OAuth2 Client Credentials Grant endpoint does not issue refresh tokens by default. This behavior is aligned with the OAuth2 specification. As a side effect of this change, no user session is created on the Keycloak server side after successful Client Credentials authentication, which results in improved performance and memory consumption. Clients that use Client Credentials Grant are encouraged to stop using refresh tokens and instead always authenticate at every request with grant_type=client_credentials instead of using refresh_token as grant type. In relation to this, Keycloak has support for revocation of access tokens in the OAuth2 Revocation Endpoint, hence clients are allowed to revoke individual access tokens if needed.

For backwards compatibility, a possibility to stick to the behavior of old versions exists. When this is used, the refresh token will be still issued after a successful authentication with the Client Credentials Grant and also the user session will be created. This can be enabled for the particular client in the Keycloak admin console, in client details in the section with OpenID Connect Compatibility Modes with the switch Use Refresh Tokens For Client Credentials Grant.

Migrating to 11.0.0

Upgrade to Wildfly 20

The Keycloak server was upgraded to use Wildfly 20 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 20 server. For example, Infinispan is now 10.1.8.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as they are not tested anymore.

  • It is recommended to use the protocolVersion property added to the remote-store element when configuring Infinispan caches. When connecting to the Infinispan server 9.4.18, the recommended version of the hotrod protocol version is 2.9 as the Infinispan library version differs among Keycloak server and Infinispan server. For more details, see the Cross-Datacenter documentation.

  • It is recommended to use remoteStoreSecurityEnabled property under connectionsInfinispan subsystem. For more details, see the Cross-Datacenter documentation.

LDAP no-import bugfix

In the previous Keycloak version, when the LDAP provider was configured with Import Users OFF, it was possible to update the user even if some of non-LDAP mapped attributes were changed. This situation resulted in confusing behavior, when the attribute appeared to be updated, but it was not. In the current version, the update is not allowed to be performed at all if any non-LDAP mapped attributes are changed.

This should not affect most of the deployments, but some can be affected under some rare circumstances. For example if you previously tried to update a user with the admin REST API and the user had some incorrect attribute changes, the update was possible. With the current version, the update is not possible and you will be immediately informed about the reason.

UserModel changes

The fields username, email, firstName and lastName in the UserModel are migrated to custom attributes as a preparation for adding more sophisticated user profiles to Keycloak in an upcoming version. If a database contains users with custom attributes of that exact name, the custom attributes will need to be migrated before upgrading. This migration does not occur automatically. Otherwise, they will not be read from the database anymore and possibly deleted. This situation implies that the username can now also be accessed and set via UserModel.getFirstAttribute(UserModel.USERNAME). Similar implications exist for other fields. Implementors of SPIs subclassing the UserModel directly or indirectly should ensure that the behavior between setUsername and setSingleAttribute(UserModel.USERNAME, …​) (and similar for the other fields) is consistent. Users of the policy evaluation feature have to adapt their policies if they use the number of attributes in their evaluations since every user will now have four new attributes by default.

The public API of UserModel did not change. No changes to frontend resources or SPIs accessing user data are necessary. Also, the database did not change yet.

Instagram IdP migrated to new the API

Instagram IdP now uses new API as the old legacy API was deprecated. This requires getting new API credentials. For details, please refer to the Server Administration Guide.

Special attention is required for existing users that use Instagram IdP, specially the ones for whom it is the only authentication option. Such users have to log in to Keycloak using Instagram IdP before September 30th, 2020. After that day they’ll have to use a different authentication method (like password) to log in to manually update their Instagram social link, or create a new account in Keycloak. This is because Instagram user IDs are not compatible between the old and new API, however the new API temporarily returns both new and old user IDs to allow migration. Keycloak automatically migrates the ID once user logs in.

Non-standard token introspection endpoint removed

In previous versions, Keycloak advertised two introspection endpoints: token_introspection_endpoint and introspection_endpoint. The latter is the one defined by RFC-8414. The former, previously deprecated, has now been removed.

Migrating to 9.0.1

Legacy promise in JavaScript adapter

It is no longer necessary to set promiseType in the JavaScript adapter, and both are available at the same time. It is recommended to update applications to use native promise API (then and catch) as soon as possible, as the legacy API (success and error) will be removed at some point.

Duplicated top level groups

Version 9.0.1 fixes a problem which could create duplicated top level groups in the realm. Nevertheless the existence of previous duplicated groups makes the upgrade process fail. The Keycloak server can be affected by this issue if it is using an H2, MariaDB, MySQL or PostgreSQL database. Before launching the upgrade, check if the server contains duplicated top level groups. For example the following SQL query can be executed at database level to list them:


Only one top level group can exist in each realm with the same name. Duplicates should be reviewed and deleted before the upgrade. The error in the upgrade contains the message Change Set META-INF/jpa-changelog-9.0.1.xml::9.0.1- KEYCLOAK-12579-add-not-null-constraint::keycloak failed.

Migrating to 9.0.0

Improved handling of user locale

A number of improvements have been made to how the locale for the login page is selected, as well as when the locale is updated for a user.

See the Server Administration Guide for more details.

Deprecated methods in token representation Java classes

In the year 2038 an int is no longer able to hold the value of seconds since 1970, as such we are working on updating these to long values. Moreover, another issue related with processing of int values exists in token representation. An int will by default result into 0 in the JSON representation, while it should not be included.

See JavaDocs for further details on exact methods that have been deprecated and replacement methods.

Migrating to 8.0.2

More authentication flows changes

REQUIRED and ALTERNATIVE executions not supported at same flow

In previous version, it was possible to have REQUIRED and ALTERNATIVE executions in the same authentication flow at the same level. There were some issues with this approach and we did the refactoring in the Authentication SPI, which means that this is not considered valid anymore. If ALTERNATIVE and REQUIRED executions are configured at the same level, the ALTERNATIVE executions are considered disabled. So when migrating to the newest version, your existing authentication flows will be automatically migrated preserved the same behavior as existed in the previous version. If they contain ALTERNATIVE executions at the same level as some REQUIRED executions, then the ALTERNATIVE executions will be added to the separate REQUIRED subflow. This should ensure same/similar behavior of the particular authentication flow as in the previous version. We always recommend to double-check the configuration of your authentication flow and test it to double-check that everything works as expected. This recommendation is true in particular if you have some more customized authentication flows with custom authenticator implementations.

Migrating to 8.0.0

New default Hostname provider

The old request and fixed hostname providers are replaced with a new default hostname provider. The request and fixed hostname providers are now deprecated and it is recommended to switch to the default hostname provider as soon as possible.

Upgrade to Wildfly 18

The Keycloak server was upgraded to use Wildfly 18 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 18 server. For example, Infinispan is now 9.4.16.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

Deploying scripts to the server

Until now, administrators were allowed to upload scripts to the server through the Keycloak Administration Console as well as through the RESTful Admin API.

For now on, this capability is disabled by default and users should prefer to deploy scripts directly to the server. For more details, please take a look at JavaScript Providers.

Client credentials in the JavaScript adapter

In the previous releases, developers were allowed to provide client credentials to the JavaScript adapter. For now on, this capability was removed, because client-side apps are not safe to keep secrets.

Authentication flows changes

We did some refactoring and improvements related to the authentication flows, which requires some attention during migration.

OPTIONAL execution requirement removed

Regarding migration, the most important change is removing the support for the OPTIONAL requirement from authentication executions and replacing it with the CONDITIONAL requirement, which allows more flexibility. The existing OPTIONAL authenticators configured in the previous version are replaced with the CONDITIONAL subflows. These subflows have the Condition - User Configured condition configured as first execution, and the previously OPTIONAL authenticator (for example OTP Form) as second execution. From the user’s point of view, the behavior during authentication is the same as in the previous version.

Changes in the Java SPI

Some changes exist in the Java Authentication SPI and Credential Provider SPI. The interface Authenticator is not changed, but you may be affected if you’re developing more advanced authenticators, which introduce some new credential types (subclasses of CredentialModel). There are changes on the CredentialProvider interface and introduction of some new interfaces like CredentialValidator. Also you may be affected if your authenticator supported the OPTIONAL execution requirement. It is recommended to double-check the latest authentication examples in the server development guide for more details.

Freemarker template changes

Some changes in the freemarker templates exist. You may be affected if you have your own theme with custom freemarker templates for login forms or some account forms, especially for the forms related to OTP. It is recommended to double-check changes in the Freemarker templates in the latest Keycloak and align your templates according to it.

User credentials changes

We added more flexibility around storing of user credentials. Among other things, every user can have multiple credentials of the same type, for example multiple OTP credentials. As a result, some changes to the database schema were performed. However the credentials from the previous version should be automatically updated to the new format and users should be still able to login with their passwords or OTP credentials set in the previous version.

Migrating to 7.0.0

Upgrade to Wildfly 17

The Keycloak server was upgraded to use Wildfly 17 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 17 server. For example, Infinispan is now 9.4.14.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

Migrating to 6.0.0

Upgrade to Wildfly 16

The Keycloak server was upgraded to use Wildfly 16 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 16 server. For example, Infinispan is now 9.4.8.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

New optional client scope

We have added a new microprofile-jwt optional client scope to handle the claims defined in the MicroProfile/JWT Auth Specification. This new client scope defines protocol mappers to set the username of the authenticated user to the upn claim and to set the realm roles to the groups claim.

Ability to propagate prompt=none to default IDP

We have added a new switch in the OIDC identity provider configuration named Accepts prompt=none forward from client to identify IDPs that are able to handle forwarded requests that include the prompt=none query parameter.

Until now, when receiving an auth request with prompt=none a realm would return a login_required error if the user is not authenticated in the realm without checking if the user has been authenticated by an IDP. From now on, if a default IDP can be determined for the auth request (either by the use of the kc_idp_hint query param or by setting up a default IDP for the realm) and if the Accepts prompt=none forward from client switch has been enabled for the IDP, the auth request is forwarded to the IDP to check if the user has been authenticated there.

It is important to note that this switch is only taken into account if a default IDP is specified, in which case we know where to forward the auth request without having to prompt the user to select an IDP. If a default IDP cannot be determined we cannot assume which one will be used to fulfill the auth request so the request forwarding is not performed.

Migrating to 5.0.0

Upgrade to Wildfly 15

The Keycloak server was upgraded to use Wildfly 15 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 15 server. For example, Infinispan is now 9.4.3.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

Migrating to 4.8.2

Google Identity Provider updated to use Google Sign-in authentication system

The Google Identity Provider implementation in Keycloak up to version 4.8.1 relies on the Google+ API endpoints for authorization and obtaining the user profile. From March 2019 onwards, Google is removing support for the Google+ API in favor of the new Google Sign-in authentication system. The Keycloak identity provider has been updated to use the new endpoints so if this integration is in use make sure you upgrade to Keycloak version 4.8.2 or later.

If you run into an error saying that the application identifier was not found in the directory, you will have to register the client application again in the Google API Console portal to obtain a new application id and secret.

It is possible that you will need to adjust custom mappers for non-standard claims that were provided by Google+ user information endpoint and are provided under different name by Google Sign-in API. Please consult Google documentation for the most up-to-date information on available claims.

LinkedIn social broker updated to Version 2 of LinkedIn APIs

Accordingly with LinkedIn, all developers need to migrate to version 2.0 of their APIs and OAuth 2.0. As such, we have updated our LinkedIn Social Broker.

Existing deployments using this broker may start experiencing errors when fetching user’s profile using version 2 of LinkedIn APIs. This error may be related with the lack of permissions granted to the client application used to configure the broker which may not be authorized to access the Profile API or request specific OAuth2 scopes during the authentication process.

Even for newly created LinkedIn client applications, you need to make sure that the client is able to request the r_liteprofile and r_emailaddress OAuth2 scopes, at least, as well that the client application can fetch current member’s profile from the https://api.linkedin.com/v2/me endpoint.

Due to these privacy restrictions imposed by LinkedIn in regards to access to member’s information and the limited set of claims returned by the current member’s Profile API, the LinkedIn Social Broker is now using the member’s email address as the default username. That means that the r_emailaddress is always set when sending authorization requests during the authentication.

Migrating to 4.6.0

New default client scopes

We have added new realm default client scopes roles and web-origins. These client scopes contain protocol mappers to add the roles of the user and allowed web origins to the token. During migration, these client scopes should be automatically added to all the OpenID Connect clients as default client scopes. Hence no setup should be required after database migration is finished.

Protocol mapper SPI addition

Related to this, a small addition to the (unsupported) Protocol Mappers SPI exists. You can be affected only if you implemented a custom ProtocolMapper. A new getPriority() method on the ProtocolMapper interface got introduced. The method has the default implementation set to return 0. If your protocol mapper implementation relies on the roles in the access token realmAccess or resourceAccess properties, you may need to increase the priority of your mapper.

Audience resolving

Audiences of all the clients, for which authenticated user has at least one client role in the token, are automatically added to the aud claim in the access token now. On the other hand, an access token may not automatically contain the audience of the frontend client, for which it was issued. Read the Server Administration Guide for more details.

JavaScript adapter promise

To use native JavaScript promise with the JavaScript adapter it is now required to set promiseType to native in the init options.

In the past if native promise was available a wrapper was returned that provided both the legacy Keycloak promise and the native promise. This was causing issues as the error handler was not always set prior to the native error event, which resulted in Uncaught (in promise) error.

Microsoft Identity Provider updated to use the Microsoft Graph API

The Microsoft Identity Provider implementation in Keycloak up to version 4.5.0 relies on the Live SDK endpoints for authorization and obtaining the user profile. From November 2018 onwards, Microsoft is removing support for the Live SDK API in favor of the new Microsoft Graph API. The Keycloak identity provider has been updated to use the new endpoints so if this integration is in use make sure you upgrade to Keycloak version 4.6.0 or later.

Legacy client applications registered under "Live SDK applications" won’t work with the Microsoft Graph endpoints due to changes in the id format of the applications. If you run into an error saying that the application identifier was not found in the directory, you will have to register the client application again in the Microsoft Application Registration portal to obtain a new application id.

Upgrade to Wildfly 14

The Keycloak server was upgraded to use Wildfly 14 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 14 server. For example, Infinispan is now 9.3.1.Final.

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

Migrating to 4.4.0

Upgrade to Wildfly 13

The Keycloak server was upgraded to use Wildfly 13 as the underlying container. This does not directly involve any specific Keycloak server functionality, however, note these changes related to migration:

Dependency updates

The dependencies were updated to the versions used by the Wildfly 13 server. For example, Infinispan is now 9.2.4.Final and Undertow is 2.0.9.Final

Configuration changes

A few configuration changes exist in the standalone(-ha).xml and domain.xml files. You should follow the Downloading the Keycloak server section to handle the migration of configuration files automatically. However, here are the most important changes, which you may need if you made your own configuration changes:

  • Element eviction on infinispan caches is now deprecated (unused) and is replaced with element object-memory

  • The cache-container element in Infinispan subsystem does not recognize the jndi-attribute anymore.

Cross-Datacenter Replication changes
  • You will need to upgrade Infinispan server to version 9.4.19. The older version may still work, but it is not guaranteed as we don’t test it anymore.

  • You don’t need to configure security anymore in the Infinispan server configuration file.

  • The transaction element needs to be removed from the configuration of replicated caches in the Infinispan server configuration file. This is needed because of the infinispan bug https://issues.redhat.com/browse/ISPN-9323.

Migration to 4.3.0

Hostname configuration

In previous versions it was recommended to use a filter to specify permitted hostnames. It is now possible to set a fixed hostname which makes it easier to make sure the valid hostname is used and also allows internal applications to invoke Keycloak through an alternative URL, for example an internal IP address. It is recommended that you switch to this approach in production.

Migrating to 4.0.0

Client Templates changed to Client Scopes

We added support for Client Scopes, which requires some attention during migration.

Client Templates changed to Client Scopes

Client Templates were changed to Client Scopes. If you had any Client Templates, their protocol mappers and role scope mappings will be preserved.

Spaces replaced in the names

Client templates with the space character in the name were renamed by replacing spaces with an underscore, because spaces are not allowed in the name of client scopes. For example, a client template my template will be changed to client scope my_template.

Linking Client Scopes to Clients

For clients which had the client template, the corresponding client scope is now added as Default Client Scope to the client. So protocol mappers and role scope mappings will be preserved on the client.

Realm Default Client Scopes not linked with existing clients

During the migration, the list of built-in client scopes is added to each realm as well as list of Realm Default Client Scopes. However, existing clients are NOT upgraded and new client scopes are NOT automatically added to them. Also all the protocol mappers and role scope mappings are kept on existing clients. In the new version, when you create a new client, it automatically has Realm Default Client Scopes attached to it and it does not have any protocol mappers attached to it. We did not change existing clients during migration as it would be impossible to properly detect customizations, which you will have for protocol mappers of the clients, for example. If you want to update existing clients (remove protocol mappers from them and link them with client scopes), you will need to do it manually.

Consents need to be confirmed again

The client scopes change required the refactoring of consents. Consents now point to client scopes, not to roles or protocol mappers. Because of this change, the previously confirmed persistent consents by users are not valid anymore and users need to confirm the consent page again after the migration.

Some configuration switches removed

The switch Scope Param Required was removed from Role Detail. The switches Consent Required and Consent Text were removed from the Protocol Mapper details. Those switches are replaced with the Client Scope feature.

Changes to Authorization Services

We added support for UMA 2.0. This version of the UMA specification introduced some important changes on how permissions are obtained from the server.

Here are the main changes introduced by UMA 2.0 support. See Authorization Services Guide for details.

Authorization API was removed

Prior to UMA 2.0 (UMA 1.0), client applications were using the Authorization API to obtain permissions from the server in the format of an RPT. The new version of UMA specification has removed the Authorization API which was also removed from Keycloak. In UMA 2.0, RPTs can now be obtained from the token endpoint by using a specific grant type. See Authorization Services Guide for details.

Entitlement API was removed

With the introduction of UMA 2.0, we decided to leverage the token endpoint and UMA grant type to allow obtaining RPTs from Keycloak and avoid having different APIs. The functionality provided by the Entitlement API was kept the same and is still possible to obtain permissions for a set of one or more resources and scopes or all permissions from the server in case no resource or scope is provided. See Authorization Services Guide for details.

Changes to UMA Discovery Endpoint

UMA Discovery document changed, see Authorization Services Guide for details.

Changes to Keycloak Authorization JavaScript adapter

The Keycloak Authorization JavaScript adapter (keycloak-authz.js) changed in order to comply with the changes introduced by UMA 2.0 while keeping the same behavior as before. The main change is on how you invoke both authorization and entitlement methods which now expect a specific object type representing an authorization request. This new object type provides more flexibility on how permissions can be obtained from the server by supporting the different parameters supported by the UMA grant type. See Authorization Services Guide for details.

One of the main changes introduced by this release is that you are no longer required to exchange access tokens with RPTs in
order to access resources protected by a resource server (when not using UMA). Depending on how the policy enforcer is configured on the resource server side, you can just send regular
access tokens as a bearer token and permissions will still be enforced.
Changes to Keycloak Authorization Client Java API

When upgrading to the new version of Keycloak Authorization Client Java API, you’ll notice that some representation classes were moved to a different package in org.keycloak:keycloak-core.

Migrating to 3.4.2

Added session_state parameter to OpenID Connect Authentication Response

The OpenID Connect Session Management specification requires that the parameter session_state is present in the OpenID Connect Authentication Response.

In past releases, we did not have this parameter, but now Keycloak adds this parameter by default, as required by the specification.

However, some OpenID Connect / OAuth2 adapters, and especially older Keycloak adapters, may have issues with this new parameter.

For example, the parameter will be always present in the browser URL after successful authentication to the client application. In these cases, it may be useful to disable adding the session_state parameter to the authentication response. This can be done for the particular client in the Keycloak admin console, in client details in the section with OpenID Connect Compatibility Modes, described in Compatibility with older adapters. Dedicated Exclude Session State From Authentication Response switch exists, which can be turned on to prevent adding the session_state parameter to the Authentication Response.

The parameter session_state was added in 3.4.2, however the switch Exclude Session State From Authentication Response was added in 4.0.0.Beta1. If your Keycloak server is on 3.4.2 or 3.4.3 and you have issues with session_state parameter, you will need to upgrade the server to 4.0.0.Beta1 or newer.

Migrating to 3.2.0

New password hashing algorithms

We’ve added two new password hashing algorithms (pbkdf2-sha256 and pbkdf2-sha512). New realms will use the pbkdf2-sha256 hashing algorithm with 27500 hashing iterations. Since pbkdf2-sha256 is slightly faster than pbkdf2 the iterations was increased to 27500 from 20000.

Existing realms are upgraded if the password policy contains the default value for hashing algorithm (not specified) and iteration (20000). If you have changed the hashing iterations you need to manually change to pbkdf2-sha256 if you’d like to use the more secure hashing algorithm.

ID Token requires scope=openid

OpenID Connect specification requires that parameter scope with value openid is used in initial authorization request. So in Keycloak 2.1.0 we changed our adapters to use scope=openid in the redirect URI to Keycloak. Now we changed the server part too and ID token will be sent to the application just if scope=openid is really used. If it’s not used, then ID token will be skipped and just Access token and Refresh token will be sent to the application. This also allows that you can omit the generation of ID Token on purpose - for example for space or performance purposes.

Direct grants (OAuth2 Resource Owner Password Credentials Grant) and Service accounts login (OAuth2 Client credentials grant) also don’t use ID Token by default now. You need to explicitly add scope=openid parameter to have ID Token included.

Authentication sessions and Action tokens

We are working on support for multiple datacenters. As the initial step, we introduced authentication session and action tokens. Authentication session replaces Client session, which was used in previous versions. Action tokens are currently used especially for the scenarios, where the authenticator or requiredActionProvider requires sending email to the user and requires user to click on the link in email.

Here are concrete changes related to this, which may affect you for the migration.

First change related to this is introducing new Infinispan caches authenticationSessions and actionTokens in standalone.xml or standalone-ha.xml. If you use our migration CLI, you don’t need to care much as your standalone(-ha).xml files will be migrated automatically.

Second change is changing of some signatures in methods of authentication SPI. This may affect you if you use custom Authenticator or RequiredActionProvider. Classes AuthenticationFlowContext and RequiredActionContext now allow to retrieve authentication session instead of client session.

We also added some initial support for sticky sessions. You may want to change your load balancer/proxy server and configure it if you don’t want to suffer from it and want to have better performance. The route is added to the new AUTH_SESSION_ID cookie. More info in the clustering documentation.

Another change is, that token.getClientSession() was removed. This may affect you for example if you’re using Client Initiated Identity Broker Linking feature.

The ScriptBasedAuthenticator changed the binding name from clientSession to authenticationSession, so you would need to update your scripts if you’re using this authenticator.

Finally we added some new timeouts to the admin console. This allows you for example to specify different timeouts for the email actions triggered by admin and by user himself.

Migrating to 2.5.1

Migration of old offline tokens

If you migrate from version 2.2.0 or older and you used offline tokens, then your offline tokens didn’t have KID in the token header. We added KID to the token header in 2.3.0 together with the ability to have multiple realm keys, so Keycloak is able to find the correct key based on the token KID.

For the offline tokens without KID, Keycloak 2.5.1 will always use the active realm key to find the proper key for the token verification. In other words, migration of old offline tokens will work. So for example, your user requested offline token in 1.9.8, then you migrate from 1.9.8 to 2.5.1 and then your user will be still able to refresh his old offline token in 2.5.1 version.

But a limitation exists. Once you change the realm active key, the users won’t be able to refresh old offline tokens anymore. So you shouldn’t change the active realm key until all your users with offline tokens refreshed their tokens. Obviously newly refreshed tokens will have KID in the header, so after all users exchange their old offline tokens, you are free to change the active realm key.

Migrating to 2.5.0

Changes to the Infinispan caches

The realms cache defined in the infinispan subsystem in standalone.xml or standalone-ha.xml configuration file, now has the eviction with the 10000 records by default. This is the same default as the users cache.

Also the authorization cache now doesn’t have any eviction on it by default.

Migrating to 2.4.0

Server SPI split into Server SPI and Sever SPI Private

The keycloak-server-spi module has been split into keycloak-server-spi and keycloak-server-spi-private. APIs within keycloak-server-spi will not change between minor releases, while we reserve the right and may quite likely change APIs in keycloak-server-spi-private between minor releases.

Key encryption algorithm in SAML assertions

Key in SAML assertions and documents are now encrypted using RSA-OAEP encryption scheme. If you want to use encrypted assertions, make sure that service providers understand this encryption scheme. In the unlikely case that SP would not be able to handle the new scheme, Keycloak can be made to use legacy RSA-v1.5 encryption scheme when started with system property keycloak.saml.key_trans.rsa_v1.5 set to true.

Infinispan caches realms and users are always local

Even if you use Keycloak in cluster, the caches realms and users defined in infinispan subsystem in standalone-ha.xml are always local caches now. A separate cache work exists, which handles sending invalidation messages between cluster nodes and informing whole cluster what records in underlying realms and users caches should be invalidated.

Migrating to 2.3.0

Default max results on paginated endpoints

All Admin REST API endpoints that support pagination now have a default max results set to 100. If you want to return more than 100 entries you need to explicitly specify that with ?max=<RESULTS>.

In 2.3.0 release we added support for Public Key Rotation. When admin rotates the realm keys in Keycloak admin console, the Client Adapter will be able to recognize it and automatically download new public key from Keycloak. However this automatic download of new keys is done just if you don’t have realm-public-key option in your adapter with the hardcoded public key. For this reason, we don’t recommend to use realm-public-key option in adapter configuration anymore.

Note this option is still supported, but it may be useful just if you really want to have hardcoded public key in your adapter configuration and never download the public key from Keycloak. In theory, one reason for this can be to avoid man-in-the-middle attack if you have untrusted network between adapter and Keycloak, however in that case, it is much better option to use HTTPS, which will secure all the requests between adapter and Keycloak.

Added Infinispan cache keys

In this release, we added new cache keys to the infinispan subsystem, which is defined in standalone.xml or standalone-ha.xml configuration file. It has also some eviction and expiration defined. This cache is internally used for caching the external public keys of the entities trusted by the server (Identity providers or clients, which uses authentication with signed JWT).

Migrating to 2.2.0

databaseSchema property deprecated

The databaseSchema property for both JPA and Mongo is now deprecated and is replaced with initializeEmpty and migrationStrategy. initializeEmpty can bet set to true or false and controls if an empty database should be initialized. migrationStrategy can be set to update, validate and manual. manual is only supported for relational databases and will write an SQL file with the required changes to the database schema. Please note that for Oracle database, the created SQL file contains SET DEFINE OFF command understood by Oracle SQL clients. Should the script be consumed by any other client, please replace the lines with equivalent command of the tool of your choice that disables variable expansion or remove it completely if such functionality is not applicable.

Changes in Client’s Valid Redirect URIs

The following scenarios are affected:

  • When a Valid Redirect URI with query component is saved in a Client (e.g. http://localhost/auth?foo=bar), redirect_uri in authorization request must exactly match this URI (or other registered URI in this Client).

  • When a Valid Redirect URI without a query component is saved in a Client, redirect_uri must exactly match as well.

  • Wildcards in registered Valid Redirect URIs are no longer supported when query component is present in this URI, so the redirect_uri needs to exactly match this saved URI as well.

  • Fragments in registered Valid Redirect URIs (like http://localhost/auth#fragment) are no longer allowed.

Authenticate by default removed from Identity Providers

Identity providers no longer has an option to set it as a default authentication provider. Instead go to Authentication, select the Browser flow and configure the Identity Provider Redirector. It has an option to set the default identity provider.

Migrating to 2.0.0

Upgrading from 1.0.0.Final no longer supported

Upgrading from 1.0.0.Final is no longer supported. To upgrade to this version upgrade to 1.9.8.Final prior to upgrading to 2.0.0.

Migrating to 1.9.5

Default password hashing interval increased to 20K

The default password hashing interval for new realms has been increased to 20K (from 1 previously). This change will have an impact on performance when users authenticate. For example with the old default (1) it takes less than 1 ms to hash a password, but with the new default (20K) the same operation can take 50-100 ms.

Migrating to 1.9.3

Add User script renamed

The script to add admin users to Keycloak has been renamed to add-user-keycloak.

Migrating to 1.9.2

Adapter option auth-server-url-for-backend-requests removed

We’ve removed the option auth-server-url-for-backend-requests due to issues in some scenarios when it was used. In more details, it was possible to access the Keycloak server from 2 different contexts (internal and external), which was causing issues in token validations etc.

If you still want to use the optimization of network, which this option provided (avoid the application to send backchannel requests through load balancer but send them to local Keycloak server directly) you may need to handle it at hosts configuration (DNS) level.

Migrating to 1.9.0

Themes and providers directory moved

We’ve moved the themes and providers directories from standalone/configuration/themes and standalone/configuration/providers to themes and providers respectively. If you have added custom themes and providers you need to move them to the new location. You also need to update keycloak-server.json as it’s changed due to this.

Adapter subsystems only bring in dependencies if Keycloak is on

Previously, if you had installed our SAML or OIDC Keycloak subsystem adapters into WildFly or JBoss EAP, we would automatically include Keycloak client jars into EVERY application irregardless if you were using Keycloak or not. These libraries are now only added to your deployment if you have Keycloak authentication turned on for that adapter (via the subsystem, or auth-method in web.xml)

Client Registration service endpoints moved

The Client Registration service endpoints have been moved from {realm-name}/clients to {realm-name}/clients-registrations.

Session state parameter in authentication response renamed

In the OpenID Connect authentication response we used to return the session state as session-state this is not correct according to the specification and has been renamed to session_state.

Deprecated OpenID Connect endpoints

In 1.2 we deprecated a number of endpoints that where not consistent with the OpenID Connect specifications, these have now been removed. This also applies to the validate token endpoint that is replaced with the new introspect endpoint in 1.8.

Updates to theme templates

Feedback in template.ftl has been moved and format has changed slightly.

Module and source code reorganization

Most of our modules and source code have been consolidated into two maven modules: keycloak-server-spi and keycloak-services. SPI interfaces are in server-spi, implementations are in keycloak-services. All JPA dependent modules have been consolidated under keycloak-model-jpa. Same goes with mongo and Infinispan under modules keycloak-model-mongo and keycloak-model-infinispan.

For adapters, session id changed after login

To plug a security attack vector, for platforms that support it (Tomcat 8, Undertow/WildFly), the Keycloak OIDC and SAML adapters will change the session id after login. You can turn off this behavior check adapter config switches.

SAML SP Client adapter changes

Keycloak SAML SP Client adapter now requires a specific endpoint, /saml to be registered with your IDP.

Migrating to 1.8.0

Admin account

In previous releases we shipped with a default admin user with a default password, this has now been removed. If you are doing a new installation of 1.8 you will have to create an admin user as a first step.

OAuth2 token introspection

In order to add more compliance with OAuth2 specification, we added a new endpoint for token introspection. The new endpoint can reached at /realms/{realm-name}/protocols/openid-connect/token/introspect and it is solely based on RFC-7662.

The /realms/{realm-name}/protocols/openid-connect/validate endpoint is now deprecated and we strongly recommend you to move to the new introspection endpoint as soon as possible. The reason for this change is that RFC-7662 provides a more standard and secure introspection endpoint.

The new token introspection URL can now be obtained from OpenID Connect Provider’s configuration at /realms/{realm-name}/.well-known/openid-configuration. There you will find a claim with name token_introspection_endpoint within the response. Only confidential clients are allowed to invoke the new endpoint, where these clients will be usually acting as a resource server and looking for token metadata in order to perform local authorization checks.

Migrating to 1.7.0.CR1

Direct access grants disabled by default for clients

In order to add more compliance with OpenID Connect specification, we added flags into admin console to Client Settings page, where you can enable/disable various kinds of OpenID Connect/OAuth2 flows (Standard flow, Implicit flow, Direct Access Grants, Service Accounts). As part of this, we have Direct Access Grants (corresponds to OAuth2 Resource Owner Password Credentials Grant) disabled by default for new clients.

Clients migrated from previous version have Direct Access Grants enabled just if they had flag Direct Grants Only on. The Direct Grants Only flag was removed as if you enable Direct Access Grants and disable both Standard+Implicit flow, you will achieve same effect.

We also added built-in client admin-cli to each realm. This client has Direct Access Grants enabled. So if you’re using Admin REST API or Keycloak admin-client, you should update your configuration to use admin-cli instead of security-admin-console as the latter one doesn’t have direct access grants enabled anymore by default.

Option 'Update Profile On First Login' moved from Identity provider to Review Profile authenticator

In this version, we added First Broker Login, which allows you to specify what exactly should be done when new user is logged through Identity provider (or Social provider), but no Keycloak user linked to the social account exists yet. As part of this work, we added option First Login Flow to identity providers where you can specify the flow and then you can configure this flow under Authentication tab in admin console.

We also removed the option Update Profile On First Login from the Identity provider settings and moved it to the configuration of Review Profile authenticator. So once you specify which flow should be used for your Identity provider (by default it’s First Broker Login flow), you go to Authentication tab, select the flow and then you configure the option under Review Profile authenticator.

Element 'form-error-page' in web.xml not supported anymore

form-error-page in web.xml will no longer work for client adapter authentication errors. You must define an error-page for the various HTTP error codes. See documentation for more details if you want to catch and handle adapter error conditions.

IdentityProviderMapper changes

The interface itself and method signatures did not change. However some changes in the behavior exist. We added First Broker Login flow in this release and the method IdentityProviderMapper.importNewUser is now called after First Broker Login flow is finished. So if you want to have any attribute available in Review Profile page, you would need to use the method preprocessFederatedIdentity instead of importNewUser . You can set any attribute by invoke BrokeredIdentityContext.setUserAttribute and that will be available on Review profile page.

Migrating to 1.6.0.Final

Option that refresh tokens are not reusable anymore

Old versions of Keycloak allowed reusing refresh tokens multiple times. Keycloak still permits this, but also have an option Revoke refresh token to disallow it. Option is under token settings in admin console. When a refresh token is used to obtain a new access token a new refresh token is also included. When option is enabled, then this new refresh token should be used next time the access token is refreshed. It won’t be possible to reuse old refresh token multiple times.

Some packages renamed

We did a bit of restructure and renamed some packages. It is mainly about renaming internal packages of util classes. The most important classes used in your application ( for example AccessToken or KeycloakSecurityContext ) as well as the SPI are still unchanged. However, a slight chance exists that you will be affected and will need to update imports of your classes. For example if you are using multitenancy and KeycloakConfigResolver, you will be affected as for example class HttpFacade was moved to different package - it is org.keycloak.adapters.spi.HttpFacade now.

Persisting user sessions

We added support for offline tokens in this release, which means that we are persisting "offline" user sessions into database now. If you are not using offline tokens, nothing will be persisted for you, so you don’t need to care about worse performance for more DB writes. However in all cases, you will need to update standalone/configuration/keycloak-server.json and add userSessionPersister like this:

"userSessionPersister": {
    "provider": "jpa"

If you want sessions to be persisted in Mongo instead of classic RDBMS, use provider mongo instead.

Migrating to 1.5.0.Final

Realm and User cache providers

Infinispan is now the default and only realm and user cache providers. In non-clustered mode a local Infinispan cache is used. We’ve also removed our custom in-memory cache and the no cache providers. If you have realmCache or userCache set in keycloak-server.json to mem or none please remove these. As Infinispan is the only provider the realmCache and userCache objects are no longer needed and can be removed.

Uses Session providers

Infinispan is now the default and only user session provider. In non-clustered mode a local Infinispan cache is used. We’ve also removed the JPA and Mongo user session providers. If you have userSession set in keycloak-server.json to mem, jpa or mongo please remove it. As Infinispan is the only provider the userSession object is no longer needed and can be removed.

For anyone that wants to achieve increased durability of user sessions this can be achieved by configuring the user session cache with more than one owner or use a replicated cache. It’s also possible to configure Infinispan to persist caches, although that would have impacts on performance.

Contact details removed from registration and account management

In the default theme we have now removed the contact details from the registration page and account management. The admin console now lists all the users attributes, not just contact specific attributes. The admin console also has the ability to add/remove attributes to a user. If you want to add contact details, please refer to the address theme included in the examples.

Migrating to 1.3.0.Final

Direct Grant API always enabled

In the past Direct Grant API (or Resource Owner Password Credentials) was disabled by default and an option to enable it on a realm existed. The Direct Grant API is now always enabled and the option to enable/disable for a realm is removed.

Database changed

There are again few database changes. Remember to back up your database prior to upgrading.

UserFederationProvider changed

There are few minor changes in UserFederationProvider interface. You may need to sync your implementation when upgrade to newer version and upgrade few methods, which has changed signature. Changes are really minor, but were needed to improve performance of federation.

WildFly 9.0.0.Final

Following on from the distribution changes that was done in the last release the standalone download of Keycloak is now based on WildFly 9.0.0.Final. This also affects the overlay which can only be deployed to WildFly 9.0.0.Final or JBoss EAP 6.4.0.GA. WildFly 8.2.0.Final is no longer supported for the server.

WildFly, JBoss EAP and JBoss AS7 adapters

There are now 3 separate adapter downloads for WildFly, JBoss EAP and JBoss AS7:

  • eap6

  • wf9

  • wf8

  • as7

Make sure you grab the correct one.

You also need to update standalone.xml as the extension module and subsystem definition has changed. See Securing Applications and Services Guide for details.

Migrating from 1.2.0.Beta1 to 1.2.0.RC1

Distribution changes

Keycloak is now available in 3 downloads: standalone, overlay and demo bundle. The standalone is intended for production and non-JEE developers. Overlay is aimed at adding Keycloak to an existing WildFly 8.2 or EAP 6.4 installation and is mainly for development. Finally we have a demo (or dev) bundle that is aimed at developers getting started with Keycloak. This bundle contains a WildFly server, with Keycloak server and adapter included. It also contains all documentation and examples.

Database changed

This release contains again a number of changes to the database. The biggest one is Application and OAuth client merge. Remember to back up your database prior to upgrading.

Application and OAuth client merge

Application and OAuth clients are now merged into Clients. The UI of admin console is updated and database as well. Your data from database should be automatically updated. The previously set Applications will be converted into Clients with Consent required switch off and OAuth Clients will be converted into Clients with this switch on.

Migrating from 1.1.0.Final to 1.2.0.Beta1

Database changed

This release contains a number of changes to the database. Remember to back up your database prior to upgrading.

iss in access and id tokens

The value of iss claim in access and id tokens have changed from realm name to realm url. This is required by OpenID Connect specification. If you’re using our adapters no change is required, except if you’ve been using bearer-only without specifying the auth-server-url, you have to add it now. If you’re using another library (or RSATokenVerifier) you need to make the corresponding changes when verifying iss.

OpenID Connect endpoints

To comply with OpenID Connect specification the authentication and token endpoints have been changed to having a single authentication endpoint and a single token endpoint. As per-spec response_type and grant_type parameters are used to select the required flow. The old endpoints (/realms/{realm-name}/protocols/openid-connect/login, /realms/{realm-name}/protocols/openid-connect/grants/access, /realms/{realm-name}/protocols/openid-connect/refresh, /realms/{realm-name}/protocols/openid-connect/access/codes) are now deprecated and will be removed in a future version.

Theme changes

The layout of themes has changed. The directory hierarchy used to be type/name this is now changed to name/type. For example a login theme named sunrise used to be deployed to standalone/configuration/themes/login/sunrise, which is now moved to standalone/configuration/themes/sunrise/login. This change was done to make it easier to have groups of the different types for the same theme into one folder.

If you deployed themes as a JAR in the past you had to create a custom theme loader which required Java code. This has been simplified to only requiring a plain text file (META-INF/keycloak-themes.json) to describe the themes included in a JAR.

Claims changes

Previously a dedicated Claims tab existed in the admin console for application and OAuth clients. This was used to configure which attributes should go into access token for particular application/client. This was removed and is replaced with protocol mappers which are more flexible.

You don’t need to care about migration of database from previous version. We did migration scripts for both RDBMS and Mongo, which should ensure that claims configured for particular application/client will be converted into corresponding protocol mappers (Still it’s safer to back up DB before migrating to newer version though). Same applies for exported JSON representation from previous version.

Social migration to identity brokering

We refactored social providers SPI and replaced it with Identity Brokering SPI, which is more flexible. The Social tab in admin console is renamed to Identity Provider tab.

Again you don’t need to care about migration of database from previous version similarly like for Claims/protocol mappers. Both configuration of social providers and "social links" to your users will be converted to corresponding Identity providers.

Only required action from you would be to change allowed Redirect URI in the admin console of particular 3rd party social providers. You can first go to the Keycloak admin console and copy Redirect URI from the page where you configure the identity provider. Then you can simply paste this as allowed Redirect URI to the admin console of 3rd party provider (IE. Facebook admin console).

Migrating from 1.1.0.Beta1 to 1.1.0.Beta2

  • adapters are now a separate download. They are not included in appliance and war distribution. We have too many now and the distro is getting bloated.

  • org.keycloak.adapters.tomcat7.KeycloakAuthenticatorValve +org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve

  • JavaScript adapter now has idToken and idTokenParsed properties. If you use idToken to retrieve first name, email, etc. you need to change this to idTokenParsed.

  • The as7-eap-subsystem and keycloak-wildfly-subsystem have been merged into one keycloak-subsystem. If you have an existing standalone.xml or domain.xml, you will need edit near the top of the file and change the extension module name to org.keycloak.keycloak-subsystem. For AS7 only, the extension module name is org.keycloak.keycloak-as7-subsystem.

  • Server installation is no longer supported on AS7. You can still use AS7 as an application client.

Migrating from 1.0.x.Final to 1.1.0.Beta1

  • RealmModel JPA and Mongo storage schema has changed

  • UserSessionModel JPA and Mongo storage schema has changed as these interfaces have been refactored

  • Upgrade your adapters, old adapters are not compatible with Keycloak 1.1. We interpreted JSON Web Token and OIDC ID Token specification incorrectly. 'aud' claim must be the client id, we were storing the realm name in there and validating it.

Migrating from 1.0 RC-1 to RC-2

  • A lot of info level logging has been changed to debug. Also, a realm no longer has the jboss-logging audit listener by default. If you want log output when users login, logout, change passwords, etc. enable the jboss-logging audit listener through the admin console.

Migrating from 1.0 Beta 4 to RC-1

  • logout REST API has been refactored. The GET request on the logout URI does not take a session_state parameter anymore. You must be logged in in order to log out the session. You can also POST to the logout REST URI. This action requires a valid refresh token to perform the logout. The signature is the same as refresh token minus the grant type form parameter. See documentation for details.

Migrating from 1.0 Beta 1 to Beta 4

  • LDAP/AD configuration is changed. It is no longer under the "Settings" page. It is now under Users→Federation. Add Provider will show you an "ldap" option.

  • Authentication SPI has been removed and rewritten. The new SPI is UserFederationProvider and is more flexible.

  • ssl-not-required +ssl-required +all +external +none

  • DB Schema has changed again.

  • Created applications now have a full scope by default. This means that you don’t have to configure the scope of an application if you don’t want to.

  • Format of JSON file for importing realm data was changed. Now role mappings is available under the JSON record of particular user.

Migrating from 1.0 Alpha 4 to Beta 1

  • DB Schema has changed. We have added export of the database to Beta 1, but not the ability to import the database from older versions. This will be supported in future releases.

  • For all clients except bearer-only applications, you must specify at least one redirect URI. Keycloak will not allow you to log in unless you have specified a valid redirect URI for that application.

  • Direct Grant API +ON

  • standalone/configuration/keycloak-server.json

  • JavaScript adapter

  • Session Timeout

Migrating from 1.0 Alpha 2 to Alpha 3

  • SkeletonKeyToken, SkeletonKeyScope, SkeletonKeyPrincipal, and SkeletonKeySession have been renamed to: AccessToken, AccessScope, KeycloakPrincipal, and KeycloakAuthenticatedSession respectively.

  • ServletOAuthClient.getBearerToken() method signature has changed. It now returns an AccessTokenResponse so that you can obtain a refresh token too.

  • adapters now check the access token expiration with every request. If the token is expired, they will attempt to invoke a refresh on the auth server using a saved refresh token.

  • Subject in AccessToken has been changed to the User ID.

Migrating from 1.0 Alpha 1 to Alpha 2

  • DB Schema has changed. We don’t have any data migration utilities yet as of Alpha 2.

  • JBoss and WildFly adapters are now installed via a WildFly subsystem. Please review the adapter installation documentation. Edits to standalone.xml are now required.

  • A new credential type "secret" got introduced. Unlike other credential types, it is stored in plain text in the database and can be viewed in the admin console.

  • Application and OAuth Client credentials are no longer required. These client types are now hard coded to use the "secret" credential type.

  • Because of the "secret" credential change to Application and OAuth Client, you’ll have to update your keycloak.json configuration files and regenerate a secret within the Application or OAuth Client credentials tab in the administration console.

Upgrading the Keycloak server

You upgrade the server before you upgrade the adapters.

Preparing for upgrading

Perform the following steps before you upgrade the server.

  1. Back up the old installation, such as configuration, themes, and so on.

  2. Handle any open transactions and delete the data/tx-object-store/ transaction directory.

  3. Back up the database using instructions in the documentation for your relational database.

    The database will no longer be compatible with the old server after you upgrade the server. If you need to revert the upgrade, first restore the old installation, and then restore the database from the backup copy.

After upgrade of Keycloak, except for offline user sessions, user sessions are lost. Users will have to log in again.

Downloading the Keycloak server

Once you have prepared for the upgrade, you can download the server.

  1. Download and extract keycloak-24.0.3.zip from the Keycloak website.

    After extracting this file, you should have a directory that is named keycloak-24.0.3.

  2. Move this directory to the desired location.

  3. Copy conf/, providers/ and themes/ from the previous installation to the new installation.

Migrating the database

Keycloak can automatically migrate the database schema, or you can choose to do it manually. By default the database is automatically migrated when you start the new installation for the first time.

Automatic relational database migration

To perform an automatic migration, start the server connected to the desired database. If the database schema has changed for the new server version, the migration starts automatically unless the database has too many records.

For example, creating an index on tables with millions of records can be time-consuming and cause a major service disruption. Therefore, a threshold of 300000 records exists for automatic migration. If the number of records exceeds this threshold, the index is not created. Instead, you find a warning in the server logs with the SQL commands that you can apply manually.

To change the threshold, set the index-creation-threshold property, value for the default connections-liquibase provider:

kc.[sh|bat] start --spi-connections-liquibase-default-index-creation-threshold=300000

Manual relational database migration

To enable manual upgrading of the database schema, set the migration-strategy property value to "manual" for the default connections-jpa provider:

kc.[sh|bat] start --spi-connections-jpa-quarkus-migration-strategy=manual

When you start the server with this configuration, the server checks if the database needs to be migrated. The required changes are written to the bin/keycloak-database-update.sql SQL file that you can review and manually run against the database.

To change the path and name of the exported SQL file, set the migration-export property for the default connections-jpa provider:

kc.[sh|bat] start --spi-connections-jpa-quarkus-migration-export=<path>/<file.sql>

For further details on how to apply this file to the database, see the documentation for your relational database. After the changes have been written to the file, the server exits.

Migrating themes

If you created custom themes, those themes must be migrated to the new server. Also, any changes to the built-in themes might need to be reflected in your custom themes, depending on which aspects you customized.

  1. Copy your custom themes from the old server themes directory to the new server themes directory.

  2. Use the following sections to migrate templates, messages, and styles.

    • If you customized any of the updated templates listed in Migration Changes, compare the template from the base theme to check for any changes you need to apply.

    • If you customized messages, you might need to change the key or value or to add additional messages.

    • If you customized any styles and you are extending the Keycloak themes, review the changes to the styles. If you are extending the base theme, you can skip this step.

Migrating templates

If you customized any template, review the new version to decide about updating your customized template. If you made minor changes, you could compare the updated template to your customized template. However, if you made many changes, consider comparing the new template to your customized template. This comparison will show you what changes you need to make.

You can use a diff tool to compare the templates. The following screenshot compares the info.ftl template from the Login theme and an example custom theme:

Updated version of a Login theme template versus a custom Login theme template

Updated version of a Login theme template versus a custom Login theme template

This comparison shows that the first change (Hello world!!) is a customization, while the second change (if pageRedirectUri) is a change to the base theme. By copying the second change to your custom template, you have successfully updated your customized template.

In an alternative approach, the following screenshot compares the info.ftl template from the old installation with the updated info.ftl template from the new installation:

Login theme template from the old installation versus the updated Login theme template

Login theme template from the old installation versus the updated Login theme template

This comparison shows what has been changed in the base template. You can then manually make the same changes to your modified template. Since this approach is more complex, use this approach only if the first approach is not feasible.

Migrating messages

If you added support for another language, you need to apply all the changes listed above. If you have not added support for another language, you might not need to change anything. You need to make changes only if you have changed an affected message in your theme.

  1. For added values, review the value of the message in the base theme to determine if you need to customize that message.

  2. For renamed keys, rename the key in your custom theme.

  3. For changed values, check the value in the base theme to determine if you need to make changes to your custom theme.

Migrating styles

You might need to update your custom styles to reflect changes made to the styles from the built-in themes. Consider using a diff tool to compare the changes to stylesheets between the old server installation and the new server installation.

For example:

$ diff KEYCLOAK_HOME_OLD/themes/keycloak/login/resources/css/login.css \

Review the changes and determine if they affect your custom styling.

Upgrading Keycloak adapters

After you upgrade the Keycloak server, you can upgrade the adapters. Earlier versions of the adapter might work with later versions of the Keycloak server, but earlier versions of the Keycloak server might not work with later versions of the adapter.

Compatibility with older adapters

Newer versions of the Keycloak server potentially work with older versions of the adapters. However, some fixes of the Keycloak server may break compatibility with older versions of the adapters. For example, a new implementation of the OpenID Connect specification may not match older client adapter versions.

For this situation, you can use Compatibility modes. For OpenID Connect clients, the Admin Console includes OpenID Connect Compatibility Modes on the page with client details. With this option, you can disable some new aspects of the Keycloak server to preserve compatibility with older client adapters. For more details, see the tool tips of individual switches.

Upgrading the EAP adapter

To upgrade the WildFly adapter, complete the following steps:

  1. Download the new adapter archive.

  2. Remove the previous adapter modules by deleting the WILDFLY_HOME/modules/system/add-ons/keycloak/ directory.

  3. Unzip the downloaded archive into WILDFLY_HOME.

Upgrading the JavaScript adapter

To upgrade a JavaScript adapter that has been copied to your web application, perform the following procedure.

  1. Download the new adapter archive.

  2. Overwrite the keycloak.js file in your application with the keycloak.js file from the downloaded archive.

Upgrading the Node.js adapter

To upgrade a Node.js adapter that has been copied to your web application, perform the following procedure.

  1. Download the new adapter archive.

  2. Remove the existing Node.js adapter directory

  3. Unzip the updated file into its place

  4. Change the dependency for keycloak-connect in the package.json of your application

Upgrading the Keycloak Admin Client

Be sure that you upgrade the Keycloak server before you upgrade the admin-client. Earlier versions of the admin-client might work with later versions of Keycloak server, but earlier versions of Keycloak server might not work with later versions of the admin-client. Therefore, use the admin-client version that matches the current Keycloak server version.