Ldap custom provider SPI

I’m using the built-in ldap user federation provider and I need to overwrite only the removeUser feature to disable the user. I know that I can create a new custom provider but
is it possibile to extends all ldap classes and modify only the part of code desired?

This is what I tried until now.

I created these files:

User Storage Provider

package com.blackpoints.storage.ldap;

import org.keycloak.models.KeycloakSession;
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.component.ComponentModel;
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;

public class LDAPStorageProvider extends org.keycloak.storage.ldap.LDAPStorageProvider
{

    public LDAPStorageProvider(LDAPStorageProviderFactory factory, KeycloakSession session, ComponentModel model, LDAPIdentityStore ldapIdentityStore) {
        super(factory,session,model,ldapIdentityStore);
    }
}

User Storage Provider Factory

package com.blackpoints.storage.ldap;


public class LDAPStorageProviderFactory extends org.keycloak.storage.ldap.LDAPStorageProviderFactory{

    @Override
    public String getId() {
        return "blackpoints-ldap";
    }

}

POM

<!--
  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
  ~ and other contributors as indicated by the @author tags.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~ http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<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/maven-v4_0_0.xsd">

    <name>Keycloak IDPM Provider</name>
    <description/>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>keycloak-idpm-ldap-provider</artifactId>
    <groupId>com.blackpoints.keycloak.storage.ldap</groupId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-core</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi-private</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-kerberos-federation</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-ldap-federation</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.9.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <version>3.4.1.Final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.transaction</groupId>
            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
            <version>1.1.1.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>idpm-ldap-provider</finalName>
    </build>
</project>

jboss deployment structure

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>  
        <dependencies>
            <module name="junit"/>
            <module name="org.keycloak.keycloak-core"/>
            <module name="org.keycloak.keycloak-server-spi"/>
            <module name="org.keycloak.keycloak-server-spi-private"/>
            <module name="org.keycloak.keycloak-kerberos-federation"/>
            <module name="org.keycloak.keycloak-ldap-federation"/>
            <module name="org.jboss.resteasy.resteasy-jaxrs"/>
            <module name="org.jboss.logging.jboss-logging"/>
            <module name="org.jboss.spec.javax.transaction.jboss-transaction-api_1.2_spec"/>
            <module name="org.keycloak.keycloak-model-jpa"/>
            <module name="org.keycloak.keycloak-common"/>
            <module name="org.keycloak.keycloak-model-infinispan"/>
            <module name="org.keycloak.keycloak-services"/>
        </dependencies>
    </deployment>  
</jboss-deployment-structure>

I create the package jar with docker image of maven executing the following command:

docker run -it --rm --name my-maven-project -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.6.1-jdk-14 mvn clean package -e

The build is successful but when copying the jar in the deployments folder the following error occurs:

keycloak_web             | 13:13:09,163 WARN  [org.jboss.modules.define] (MSC service thread 1-1) Failed to define class com.blackpoints.storage.ldap.LDAPStorageProviderFactory in Module "deployment.idpm-ldap-provider.jar" from Service Module Loader: java.lang.NoClassDefFoundError: Failed to link com/blackpoints/storage/ldap/LDAPStorageProviderFactory (Module "deployment.idpm-ldap-provider.jar" from Service Module Loader): org/keycloak/storage/ldap/LDAPStorageProviderFactory
keycloak_web             |      at java.lang.ClassLoader.defineClass1(Native Method)
keycloak_web             |      at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
keycloak_web             |      at java.lang.ClassLoader.defineClass(ClassLoader.java:839)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.doDefineOrLoadClass(ModuleClassLoader.java:424)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:555)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:339)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:126)
keycloak_web             |      at org.jboss.modules.Module.loadModuleClass(Module.java:731)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:247)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
keycloak_web             |      at java.lang.Class.forName0(Native Method)
keycloak_web             |      at java.lang.Class.forName(Class.java:348)
keycloak_web             |      at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:370)
keycloak_web             |      at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
keycloak_web             |      at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
keycloak_web             |      at org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
keycloak_web             |      at org.keycloak.provider.ProviderManager.load(ProviderManager.java:92)
keycloak_web             |      at org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:214)
keycloak_web             |      at org.keycloak.services.DefaultKeycloakSessionFactory.deploy(DefaultKeycloakSessionFactory.java:115)
keycloak_web             |      at org.keycloak.provider.ProviderManagerRegistry.deploy(ProviderManagerRegistry.java:42)
keycloak_web             |      at org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor.deploy(KeycloakProviderDeploymentProcessor.java:55)
keycloak_web             |      at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:176)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1737)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1699)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1557)
keycloak_web             |      at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
keycloak_web             |      at java.lang.Thread.run(Thread.java:748)
keycloak_web             | 
keycloak_web             | 13:13:09,169 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC000001: Failed to start service jboss.deployment.unit."idpm-ldap-provider.jar".POST_MODULE: org.jboss.msc.service.StartException in service jboss.deployment.unit."idpm-ldap-provider.jar".POST_MODULE: WFLYSRV0153: Failed to process phase POST_MODULE of deployment "idpm-ldap-provider.jar"
keycloak_web             |      at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:183)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1737)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1699)
keycloak_web             |      at org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1557)
keycloak_web             |      at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
keycloak_web             |      at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
keycloak_web             |      at java.lang.Thread.run(Thread.java:748)
keycloak_web             | Caused by: java.lang.NoClassDefFoundError: Failed to link com/blackpoints/storage/ldap/LDAPStorageProviderFactory (Module "deployment.idpm-ldap-provider.jar" from Service Module Loader): org/keycloak/storage/ldap/LDAPStorageProviderFactory
keycloak_web             |      at java.lang.ClassLoader.defineClass1(Native Method)
keycloak_web             |      at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
keycloak_web             |      at java.lang.ClassLoader.defineClass(ClassLoader.java:839)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.doDefineOrLoadClass(ModuleClassLoader.java:424)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:555)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:339)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:126)
keycloak_web             |      at org.jboss.modules.Module.loadModuleClass(Module.java:731)
keycloak_web             |      at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:247)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
keycloak_web             |      at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
keycloak_web             |      at java.lang.Class.forName0(Native Method)
keycloak_web             |      at java.lang.Class.forName(Class.java:348)
keycloak_web             |      at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:370)
keycloak_web             |      at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
keycloak_web             |      at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
keycloak_web             |      at org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
keycloak_web             |      at org.keycloak.provider.ProviderManager.load(ProviderManager.java:92)
keycloak_web             |      at org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:214)
keycloak_web             |      at org.keycloak.services.DefaultKeycloakSessionFactory.deploy(DefaultKeycloakSessionFactory.java:115)
keycloak_web             |      at org.keycloak.provider.ProviderManagerRegistry.deploy(ProviderManagerRegistry.java:42)
keycloak_web             |      at org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor.deploy(KeycloakProviderDeploymentProcessor.java:55)
keycloak_web             |      at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:176)
keycloak_web             |      ... 8 more
keycloak_web             | 
keycloak_web             | 13:13:09,171 ERROR [org.jboss.as.controller.management-operation] (DeploymentScanner-threads - 2) WFLYCTL0013: Operation ("full-replace-deployment") failed - address: ([]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"idpm-ldap-provider.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"idpm-ldap-provider.jar\"
keycloak_web             |     Caused by: java.lang.NoClassDefFoundError: Failed to link com/blackpoints/storage/ldap/LDAPStorageProviderFactory (Module \"deployment.idpm-ldap-provider.jar\" from Service Module Loader): org/keycloak/storage/ldap/LDAPStorageProviderFactory"}}

Good Day,
did you find any solution to your problem?
i currently have a similar one.

Nope. In the end I let keycloak delete user and with a sync script I update the column deleted of the
deleted user. To clarify I maintain another users table in my application which is synchronized every hour with keycloak.

1 Like

Hi, I had the same issue.
You need to bundle org.keycloak.keycloak-ldap-federation dependency into the final jar. For some reason the LDAPStorageProviderFactory class in not on classpath on runtime.
However, after that I’ve run into other error:

FATAL [org.keycloak.services] (ServerService Thread Pool -- 64) java.util.ServiceConfigurationError: org.keycloak.storage.ldap.mappers.LDAPStorageMapperFactory: org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapperFactory not a subtype

after I added provided on every keycloak dependency on pom.xml I get it to working

pom.xml:

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>digital.koerber</groupId>
	<artifactId>custom-ldap-spi</artifactId>
	<version>1.0.0</version>

	<name>Custom LDAP Provider</name>
	<description />
	<packaging>jar</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-core</artifactId>
			<version>7.0.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-server-spi</artifactId>
			<version>7.0.1</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-server-spi-private</artifactId>
			<version>7.0.1</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-kerberos-federation</artifactId>
			<version>7.0.1</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.keycloak</groupId>
			<artifactId>keycloak-ldap-federation</artifactId>
			<version>7.0.1</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.jboss.resteasy</groupId>
			<artifactId>resteasy-jaxrs</artifactId>
			<version>3.9.0.Final</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.jboss.logging</groupId>
			<artifactId>jboss-logging</artifactId>
			<version>3.4.1.Final</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
						<scope>provided</scope>
			
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.transaction</groupId>
			<artifactId>jboss-transaction-api_1.2_spec</artifactId>
			<version>1.1.1.Final</version>
						<scope>provided</scope>
			
		</dependency>
		
		<dependency>
			<groupId>org.jboss.spec.javax.ejb</groupId>
			<artifactId>jboss-ejb-api_3.2_spec</artifactId>
			<version>2.0.0.Final</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>custom-ldap-provider</finalName>
		<plugins>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
			</plugin>
		</plugins>

	</build>


</project>

CustomLdapStorageProvider.java

package foo.bar.lion.storage.ldap;

import javax.ejb.Remove;

import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.storage.ldap.LDAPStorageProvider;
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;


public class CustomLDAPStorageProvider extends LDAPStorageProvider {

	private static final Logger logger = Logger.getLogger(CustomLDAPStorageProvider.class);
	public CustomLDAPStorageProvider(CustomLDAPStorageProviderFactory factory, KeycloakSession session,
			ComponentModel model, LDAPIdentityStore ldapIdentityStore) {
		super(factory, session, model, ldapIdentityStore);
	}

	@Override
	public UserModel validate(RealmModel realm, UserModel local) {
		try {
			logger.error("#######   VALIDATE USER");

			return super.validate(realm, local);
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}

	@Override
	public boolean validPassword(RealmModel realm, UserModel user, String password) {
		try {
			logger.error("#######   VALIDATE password");

			return super.validPassword(realm, user, password);
		} catch (Exception e) {
			logger.error(e);
			return false;
		}
	}

	@Override
	public CredentialValidationOutput authenticate(RealmModel realm, CredentialInput cred) {
		try {
			logger.error("#######   AUTH");
			return super.authenticate(realm, cred);

		} catch (Exception e) {
			logger.error(e);
			return CredentialValidationOutput.failed();
		}
	}

	@Remove
	@Override
	public void close() {
		// according to
		// https://www.keycloak.org/docs/latest/server_development/#leveraging-java-ee
	}

}

CustomLDAPStorageProviderFactory.java

package foo.bar.lion.storage.ldap;

import java.util.LinkedHashMap;
import java.util.Map;

import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.storage.ldap.LDAPIdentityStoreRegistry;
import org.keycloak.storage.ldap.LDAPStorageProvider;
import org.keycloak.storage.ldap.LDAPStorageProviderFactory;
import org.keycloak.storage.ldap.idm.store.ldap.LDAPIdentityStore;
import org.keycloak.storage.ldap.mappers.LDAPConfigDecorator;

public class CustomLDAPStorageProviderFactory extends LDAPStorageProviderFactory
		implements ServerInfoAwareProviderFactory {
	private LDAPIdentityStoreRegistry ldapStoreRegistry;

	@Override
	public String getId() {
		return "lion-ldap";
	}

	@Override
	public LDAPStorageProvider create(KeycloakSession session, ComponentModel model) {
		Map<ComponentModel, LDAPConfigDecorator> configDecorators = getLDAPConfigDecorators(session, model);

		LDAPIdentityStore ldapIdentityStore = this.ldapStoreRegistry.getLdapStore(session, model, configDecorators);
		return new CustomLDAPStorageProvider(this, session, model, ldapIdentityStore);
	}

	@Override
	public void init(Config.Scope config) {
		this.ldapStoreRegistry = new LDAPIdentityStoreRegistry();
	}

	@Override
	public Map<String, String> getOperationalInfo() {
		Map<String, String> ret = new LinkedHashMap<>();
		ret.put("custom-ldap", "lion-ldap");
		return ret;
	}

}

src/main/resources/META-INF/jboss-deployment-structure.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
	<deployment>
		<dependencies>

			<module name="org.keycloak.keycloak-core" />
			<module name="org.keycloak.keycloak-server-spi" />
			<module name="org.keycloak.keycloak-server-spi-private" />
			<module name="org.keycloak.keycloak-kerberos-federation" />
			<module name="org.keycloak.keycloak-ldap-federation" />
<!-- 			<module name="org.jboss.resteasy.resteasy-jaxrs" /> -->
<!-- 			<module name="org.jboss.logging.jboss-logging" /> -->
<!-- 			<module -->
<!-- 				name="org.jboss.spec.javax.transaction.jboss-transaction-api_1.2_spec" /> -->
			<module name="org.keycloak.keycloak-model-jpa" />
			<module name="org.keycloak.keycloak-common" />
			<module name="org.keycloak.keycloak-model-infinispan" />
			<module name="org.keycloak.keycloak-services" />
<!-- 			<module -->
<!-- 				name="org.keycloak.federation.kerberos.KerberosFederationProviderFactory" /> -->
		</dependencies>
	</deployment>
</jboss-deployment-structure>

src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory

foo.bar.lion.storage.ldap.CustomLDAPStorageProviderFactory

then

mvn clean install assembly:single

copy jar-with-dependencies.jar to {keycloak_home}\standalone\deployments

1 Like