Not able to register custom SPI as module

1,027 views
Skip to first unread message

Cristina Pistacho

unread,
Feb 2, 2021, 8:11:07 AM2/2/21
to Keycloak User
Hello,

I'm having some trouble registering a custom SPI using modules and I've been struggling for several days with no success at all. If I use the keycloak deployer, I'm able to see my provider, but it crashes because it cannot find some dependencies. In the documentation it sais "If you are creating a custom SPI you will need to deploy it as a module, otherwise we recommend using the Keycloak deployer approach."
I run keycloak in a docker container. Here's my docker compose file:

docker-compose.yml
version: '3'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres
      volumes:
        - postgres_data:/var/lib/postgresql/data
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
  keycloak:
      environment:
        DB_VENDOR: POSTGRES
        DB_ADDR: postgres
        DB_DATABASE: keycloak
        DB_USER: keycloak
        DB_SCHEMA: public
        DB_PASSWORD: password
        KEYCLOAK_USER: cristina
        KEYCLOAK_PASSWORD: cristina
      ports:
        - 8080:8080
      depends_on:
        - postgres

I've created a java project from scratch with maven. This is my pom.xml:

pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>org.cristina</groupId>
<artifactId>cristina-validator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<finalName>cristinaValidator</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptors>
<descriptor>src/assembly/assemblyModule.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>12.0.2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
<version>12.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
<version>12.0.2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>12.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-core-spi</artifactId>
<version>4.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-spi</artifactId>
<version>12.0.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>

I've created the package org.cristina.authentication.forms inside src/main/java. Inside this folder, I've added the java class CristinaRegistration.java, which is an implementation of FormAction and FormActionFactory.

CristinaRegistration.java
package org.cristina.authentication.forms;

import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.MultivaluedMap;

import org.keycloak.Config;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormContext;
import org.keycloak.authentication.forms.RegistrationPage;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;

public class CristinaRegistration implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "cristina-registration-action";

...

}

Here's what I've added in standalone.xml, inside the keycloak server subsystem:

standalone.xml
<providers>
    <provider>
        classpath:${jboss.home.dir}/providers/*
    </provider>
    <provider>
        module:org.cristina.authentication.forms
    </provider>
</providers>

As far as I know, I have to create a configuration file so that keycloak can scan mi provider. The file is inside src/main/resources/META-INF/services folder:

org.keycloak.authentication.FormActionFactory
org.cristina.authentication.forms.CristinaRegistration

I've created a folder named opt/jboss/keycloak/modules/org/cristina/authentication/forms/main inside my keycloak container and I've placed my jar cristinaValidator.jar and this module.xml file:

module.xml
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="org.cristina.authentication.forms">
    <resources>
        <resource-root path="registerProvider.jar"/>
    </resources>
    <dependencies>
        <module name="org.keycloak.keycloak-server-spi" services="import"/>
            <module name="org.keycloak.keycloak-server-spi-private" services="import"/>
            <module name="org.keycloak.keycloak-services"/>
            <module name="org.jboss.resteasy.resteasy-core-spi"/>
            <module name="org.keycloak.keycloak-common"/>
            <module name="org.keycloak.keycloak-core"/>
            <module name="javax.ws.rs-api"/>
    </dependencies>
</module>

Then, I restart my container, go to the administration console, I sign in with the admin credentials which are username: cristina pwd: cristina. I go to Authentication, I select the registration flow, I create a copy from it and I select "add executions". The problem is that I cannot see my registered providers in the providers list.


Thanks for your time.

Regards, 
Cristina

Quentin Mino

unread,
Feb 15, 2021, 10:52:58 AM2/15/21
to Keycloak User
Hi,
I never done this inside a container but when we are developping some custom SPI like these, we put the jar file inside the /standalone/deployments folder of keycloak. It can be don while Keycloak is running and will do a hot reload.
Maybe this can work inside the container too.

Regards,

benjam...@gmail.com

unread,
Feb 16, 2021, 9:37:24 AM2/16/21
to Keycloak User
What are the specific dependencies that are missing? If it's crashing on missing libraries, maybe try this in your pom.xml under maven-assembly-plugin:

      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <id>install</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <archive>
            <manifest>
              <mainClass>fully.qualified.MainClass</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>

When you compile you should see also a build/target/cristinaValidator--jar-with-dependencies.jar created - use this jar instead in the container's providers/. This resolved an issue early on for me with some missing libraries.

Reply all
Reply to author
Forward
0 new messages