'How to use keycloak-admin-client with custom Keycloak provider

I am building a docker image based on Keycloak 18 with my custom provider which uses Keycloak Admin REST Client. I figured that I need to bundle some dependencies as well so I created jar files with maven-dependency-plugin and included those as well.

Here's my Dockerfile:

FROM maven:3.6.0-jdk-11-slim AS javabuild
ENV HOME=/home/app
RUN mkdir -p $HOME
WORKDIR $HOME

ADD pom.xml $HOME
ADD my-provider/pom.xml $HOME/my-provider/pom.xml

RUN mvn -pl my-provider verify --fail-never
ADD my-provider $HOME/my-provider
RUN rm -rf ${HOME}/my-provider/target
RUN mvn -pl my-provider install
RUN mvn -pl my-provider package


FROM quay.io/keycloak/keycloak:18.0.0 as builder
COPY --from=javabuild /home/app/my-provider/target/my-provider*.jar /opt/keycloak/providers/
COPY --from=javabuild /home/app/my-provider/target/dependency/*.jar /opt/keycloak/providers/

RUN /opt/keycloak/bin/kc.sh build


FROM quay.io/keycloak/keycloak:18.0.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
WORKDIR /opt/keycloak

ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start-dev", "--db=postgres"]

I have difficult time to understand what I should exactly provide and how.

I tried to use keycloak-admin-client directly and only add that as dependency and no transient dependecies as it seems that Keycloak already has these dependencies available as since Keycloak 17 they use Quarkus. I don't know much about Quarkus but looking into docker container files I found:

bash-4.4$ ls | grep "rest"
com.openshift.openshift-restclient-java-8.0.0.Final.jar
io.quarkus.quarkus-resteasy-2.7.5.Final.jar
io.quarkus.quarkus-resteasy-common-2.7.5.Final.jar
io.quarkus.quarkus-resteasy-jackson-2.7.5.Final.jar
io.quarkus.quarkus-resteasy-server-common-2.7.5.Final.jar
org.jboss.resteasy.resteasy-core-4.7.5.Final.jar
org.jboss.resteasy.resteasy-core-spi-4.7.5.Final.jar
org.jboss.resteasy.resteasy-jackson2-provider-4.7.5.Final.jar
org.jboss.resteasy.resteasy-jaxb-provider-4.7.5.Final.jar
org.jboss.resteasy.resteasy-multipart-provider-4.7.5.Final.jar

If I run my provider the following in my provider:

public class MyProvider implements RealmResourceProvider {
    private final Keycloak keycloak;

    public MyProvider(KeycloakSession session) {
        RealmModel realm = session.getContext().getRealm();

        this.keycloak = KeycloakBuilder.builder()
                .serverUrl("http://localhost:8080")
                .realm(realm.getId())
                .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
                .clientId("admin-cli")
                .clientSecret("...")
                .build();

        UserResource user = this.keycloak.realm(realm.getId()).users().get("admin");
        System.out.println(user);
    }
}

I will get

Uncaught server error: java.lang.ExceptionInInitializerError

Then I realized that maybe I should add org.jboss.resteasy.resteasy-client:4.7.5-Final as well. Then I got

Uncaught server error: java.lang.NoClassDefFoundError: org/jboss/resteasy/client/jaxrs/ResteasyClientBuilder

Then I tried to add resteasy-client dependencies as well so after adding org.jboss.resteasy.resteasy-client-api:4.7.5-Final I got

Uncaught server error: java.util.ServiceConfigurationError: org.jboss.resteasy.client.jaxrs.spi.ClientConfigProvider: org.wildfly.security.auth.client.spi.RESTEasyClientConfigProviderImpl Unable to get public no-arg constructor
...
Caused by: java.lang.NoClassDefFoundError: org/wildfly/client/config/ConfigXMLParseException
...
Caused by: java.lang.ClassNotFoundException: org.wildfly.client.config.ConfigXMLParseException

Then I realized that keycloack-admin-client actually relies on resteasy-client version 3.13.2.Final and not 4.7.5-Final. So I add these versions of resteasy-client, resteasy-multipart-provider, resteasy-jackson2-provider and resteasy-jaxb-provider and got

Uncaught server error: java.lang.InstantiationError: org.jboss.resteasy.spi.ResteasyProviderFactory

And now I have no idea what to try next.

I found this issue report which talks about Resteasy version conflict between Keycloak Admin REST Client and Quarkus and it mentions there's another package called quarkus-keycloack-admin-client. So I added that as dependency and again I got:

Uncaught server error: java.lang.NoClassDefFoundError: org/keycloak/admin/client/KeycloakBuilder

also quarkus-keycloack-admin-client is dependend on Keycloak 17 so I am not sure if it's 100% compatible with 18.

So my question is how I should include keycloack-admin-client in my provider?



Solution 1:[1]

My Problems are very similar and I will try to build a customized quarkus-keycloak-server (https://blog.codecentric.de/2022/05/keycloak-x-aber-sicher-ohne-bekannte-sicherheitsluecken/) extended with dependencies needed by my own extensions. I dont know, if this will lead to a running system, but I'll hope so.

Solution 2:[2]

I had the same problem as you and I created a Keycloak ticket here: https://github.com/keycloak/keycloak/issues/11973

I solved this problem by myself by adding this:

  1. I overrode the resteasy-client with version 4.7.5.Final.

    <!--Keycloak-->
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-admin-client</artifactId>
        <scope>provided</scope>
        <exclusions>
            <exclusion>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-client</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-multipart-provider</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jackson2-provider</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.jboss.resteasy</groupId>
                <artifactId>resteasy-jaxb-provider</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--Resteasy-->
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-client</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-multipart-provider</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jackson2-provider</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxb-provider</artifactId>
        <scope>provided</scope>
    </dependency>
    
  2. Copied all third-party dependencies to /providers folder

  3. Because I got errors looks like:

    Uncaught server error: java.util.ServiceConfigurationError: org.jboss.resteasy.client.jaxrs.spi.ClientConfigProvider: org.wildfly.security.auth.client.spi.RESTEasyClientConfigProviderImpl Unable to get public no-arg constructor

    Caused by: java.lang.ClassNotFoundException: org.wildfly.client.config.ConfigXMLParseException

  4. I found the dependency for this class and add this, it works for me :)

    <dependency>
        <groupId>org.wildfly.client</groupId>
        <artifactId>wildfly-client-config</artifactId>
        <version>1.0.1.Final</version>
    </dependency>
    

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 thsppi
Solution 2 LĂȘ H?ng Quy?n