When migrating multiple legacy auth systems to Keycloak it's useful to read credentials from an existing database (that being MySQL, Oracle, PostgreSQL, etc) in an on-demand way.
Keycloak quick-start user-storage-jpa provides a great starting point for this, but no indications on how to read from multiple/dynamic JPA datasources.
This projects aims to provide a Keycloak User Storage Provider able to use different databases. As an example:
- User Storage Provider A => reading from DB-A
- User Storage Provider B => reading from DB-B
- User Storage Provider C => reading from DB-C
- tested on Keycloak 26.0.5
- Java SDK 17.0 or later (Maven 3)
You must first create the Quarkus datasource that will be used. You can add them in <keycloak>/conf/quarkus.properties
:
quarkus.datasource.user-store.db-kind=h2
quarkus.datasource.user-store.username=sa
quarkus.datasource.user-store.jdbc.url=jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1`
quarkus.datasource.user-store.jdbc.transactions=xa
quarkus.datasource.user-store2.db-kind=h2
quarkus.datasource.user-store2.username=sa
quarkus.datasource.user-store2.jdbc.url=jdbc:h2:mem:user-store;DB_CLOSE_DELAY=-1`
quarkus.datasource.user-store2.jdbc.transactions=xa
Please note: quarkus.properties
is not the one in the extension, but in the Keycloak server conf
folder
Differently from the previously JBoss-based Keycloak, in Quarkus
you also need to explicitly maintain datasources in src/main/resources/META-INF/persistence.xml
by adding a corresponding <persistence-unit>
for each datasource.
<persistence-unit name="user-store" transaction-type="JTA">
<class>org.keycloak.multipleds.storage.user.entities.UserEntity</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<!-- Sets the name of the datasource to be the same as the datasource name in quarkus.properties-->
<property name="hibernate.connection.datasource" value="user-store"/>
<property name="javax.persistence.transactionType" value="JTA"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="user-store2" transaction-type="JTA">
<class>org.keycloak.multipleds.storage.user.entities.UserEntity</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<!-- Sets the name of the datasource to be the same as the datasource name in quarkus.properties-->
<property name="hibernate.connection.datasource" value="user-store2"/>
<property name="javax.persistence.transactionType" value="JTA"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
Compile with mvn package
or, if you have Docker make package
. Then copy target/multiple-ds-user-storage.jar
to <keycloak>/providers/
folder
- Login to the Keycloak Admin Console
- access User Federation tab
- add a new User Storage provider instance selecting
multiple-ds-user-storage
from the list box
- save and repeat for as many datasources you need
- password validation can be customized in
MultipleDSUserStorageProvider.isValid
(by default: salted SHA-1) - password update can be customized in
MultipleDSUserStorageProvider.updateCredential
- once password is changed in Keycloak, the legacy password is not used anymore
- once password is changed in Keycloak, it's persisted in the legacy DB as well - this to provide a fallback if Keycloak implementation must be rolled-back