I’m trying to add a simple custom user storage SPI in keycloak 17.0.0. I have followed the keycloak documentation for creation of custom user storage SPI, But when I try to build kc I’m getting error:
ERROR: Failed to run 'build' command.
ERROR: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[error]: Build step org.keycloak.quarkus.deployment.KeycloakProcessor#configureProviders threw an exception: java.util.ServiceConfigurationError: org.keycloak.theme.ThemeSelectorProviderFactory: keycloak.spi.test.file.PropertyFileUserStorageProviderFactory not a subtype
at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:589)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1237)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1265)
at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1300)
at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1385)
at org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
at org.keycloak.provider.ProviderManager.load(ProviderManager.java:94)
at org.keycloak.quarkus.deployment.KeycloakProcessor.loadFactories(KeycloakProcessor.java:456)
at org.keycloak.quarkus.deployment.KeycloakProcessor.configureProviders(KeycloakProcessor.java:254)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:882)
at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
at java.base/java.lang.Thread.run(Thread.java:829)
at org.jboss.threads.JBossThread.run(JBossThread.java:501)
ERROR: Build failure: Build failed due to errors
[error]: Build step org.keycloak.quarkus.deployment.KeycloakProcessor#configureProviders threw an exception: java.util.ServiceConfigurationError: org.keycloak.theme.ThemeSelectorProviderFactory: keycloak.spi.test.file.PropertyFileUserStorageProviderFactory not a subtype
at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:589)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1237)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1265)
at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1300)
at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1385)
at org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
at org.keycloak.provider.ProviderManager.load(ProviderManager.java:94)
at org.keycloak.quarkus.deployment.KeycloakProcessor.loadFactories(KeycloakProcessor.java:456)
at org.keycloak.quarkus.deployment.KeycloakProcessor.configureProviders(KeycloakProcessor.java:254)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:882)
at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
at java.base/java.lang.Thread.run(Thread.java:829)
at org.jboss.threads.JBossThread.run(JBossThread.java:501)
ERROR: org.keycloak.theme.ThemeSelectorProviderFactory: keycloak.spi.test.file.PropertyFileUserStorageProviderFactory not a subtype
For more details run the same command passing the '--verbose' option. Also you can use '--help' to see the details about the usage of the particular command.
POM.xml content is:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>keycloack-SPI-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>17.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<version>17.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<finalName>my-user-provider</finalName>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</plugin>
</plugins>
</build>
</project>
I’m creating jar with mvn command mvn clean compile assembly:single and placing that jar in providers directory.
Content of my UserStorageFactory class is given below:
public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {
private static final String PROVIDER_NAME = "property-file";
protected Properties properties = new Properties();
private static final Logger logger = Logger.getLogger(PropertyFileUserStorageProviderFactory.class.getName());
@Override
public PropertyFileUserStorageProvider create(KeycloakSession keycloakSession, ComponentModel componentModel) {
return new PropertyFileUserStorageProvider(keycloakSession, componentModel, properties);
}
@Override
public String getId() {
return PROVIDER_NAME;
}
@Override
public void init(Config.Scope config) {
String path = config.get("path");
InputStream is = getClass().getClassLoader().getResourceAsStream(path);
if (is == null) {
logger.severe("Could not find users.properties in classpath");
} else {
try {
properties.load(is);
} catch (IOException ex) {
logger.severe("Failed to load users.properties file" + ex);
}
}
}
@Override
public void close() {
UserStorageProviderFactory.super.close();
}
@Override
public void postInit(KeycloakSessionFactory factory) {
UserStorageProviderFactory.super.postInit(factory);
}
@Override
public UserStorageProvider create(KeycloakSession session) {
return UserStorageProviderFactory.super.create(session);
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return UserStorageProviderFactory.super.getConfigProperties();
}
@Override
public String getHelpText() {
return UserStorageProviderFactory.super.getHelpText();
}
@Override
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException {
UserStorageProviderFactory.super.validateConfiguration(session, realm, config);
}
@Override
public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
UserStorageProviderFactory.super.onCreate(session, realm, model);
}
@Override
public List<ProviderConfigProperty> getCommonProviderConfigProperties() {
return UserStorageProviderFactory.super.getCommonProviderConfigProperties();
}
@Override
public Map<String, Object> getTypeMetadata() {
return UserStorageProviderFactory.super.getTypeMetadata();
}
I'm not sure what I'm doing wrong.