Hi,
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 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
error.
The screenshot of this error is given below:
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();
}