This is a demonstration on how to connect keycloak to a out-of-the-box unsupported user storage type/format. (For demonstration purposes an external MySQL database)
The solution demonstrated in this branch uses manually constructed JPA connection. The reason behind this architectural decision is the way connections are usually established when using the Java Persistence API. Usually there's a persistence.xml
-file deployed on the application server bundled with the application.
This has one major drawback - Connections are hardcoded and providers to different databases require multiple deployments.
To utilize the on-the-fly runtime configuration of providers in Keycloak, we need a way to construct JPA connections at runtime.
./gradlew jar
and wait till it's finished processingkeycloak-user-store-1.0.0-SNAPSHOT.jar
from your ./build/libs/
-folder to <pathToKeycloak>/standalone/deployments/
3306
.We had the same problem and came to the conclusion that it wasn't meant to be used this way. By reverse engineering different frameworks that utilize JPA we found a workaround, which has one immense drawback. It uses the Hibernate built-in connection pool, which is not recommended for production use. Plus a lot of other drawbacks, like the need for manual transaction handling.
To start connections at runtime you need to create an object of javax.persistence.spi.PersistenceUnitInfo
. Then you can generate a EntityManagerFactory
to create connections from:
MultivaluedHashMap<String, String> config = model.getConfig();
properties.put("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver");
properties.put("hibernate.connection.url",
String.format("jdbc:mysql://%s:%s/%s",
config.getFirst(DB_HOST_KEY),
config.getFirst(DB_PORT_KEY),
config.getFirst(DB_DATABASE_KEY)));
properties.put("hibernate.connection.username", config.getFirst(DB_USERNAME_KEY));
properties.put("hibernate.connection.password", config.getFirst(DB_PASSWORD_KEY));
properties.put("hibernate.show-sql", "true");
properties.put("hibernate.archive.autodetection", "class, hbm");
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.connection.autocommit", "true");
entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(getPersistenceUnitInfo("h2userstorage"), properties);
This problem occurs due to manually constructing the JPA connection. You need to manually start a transaction and persist entities once you modified them.
For further reference, please consider the following sections: