I’m trying to provide my own custom SocialidentityProviderFactory that points to GSuite instead of Google.
The problem I’m having though is on start up I’m receiving this error:
com.mycompany.keycloak.useridmapper.GSuiteIdentityProviderFactory Unable to get public no-arg constructor
Full stacktrace:
Unable to get public no-arg constructor
at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:581)
at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:672)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1232)
at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1264)
at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1299)
at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1384)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.provider.ProviderManager.load(ProviderManager.java:95)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:256)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.services.DefaultKeycloakSessionFactory.init(DefaultKeycloakSessionFactory.java:96)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.services.resources.KeycloakApplication.createSessionFactory(KeycloakApplication.java:260)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.services.resources.KeycloakApplication.startup(KeycloakApplication.java:125)
at org.keycloak.keycloak-wildfly-extensions@12.0.1//org.keycloak.provider.wildfly.WildflyPlatform.onStartup(WildflyPlatform.java:29)
at org.keycloak.keycloak-services@12.0.1//org.keycloak.services.resources.KeycloakApplication.<init>(KeycloakApplication.java:115)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:152)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2815)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:371)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.spi.ResteasyDeployment.startInternal(ResteasyDeployment.java:283)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:93)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:140)
at org.jboss.resteasy.resteasy-jaxrs@3.13.2.Final//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:42)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:117)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.security.RunAsLifecycleInterceptor.init(RunAsLifecycleInterceptor.java:78)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:103)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start(ManagedServlet.java:305)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.ManagedServlet.createServlet(ManagedServlet.java:145)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:588)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:559)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1530)
at io.undertow.servlet@2.2.2.Final//io.undertow.servlet.core.DeploymentManagerImpl.start(DeploymentManagerImpl.java:601)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:97)
at org.wildfly.extension.undertow@21.0.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:78)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.JBossThread.run(JBossThread.java:513)
Caused by: java.lang.NoClassDefFoundError: org/keycloak/social/google/GoogleIdentityProviderConfig
at java.base/java.lang.Class.getDeclaredConstructors0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3137)
at java.base/java.lang.Class.getConstructor0(Class.java:3342)
at java.base/java.lang.Class.getConstructor(Class.java:2151)
at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:659)
at java.base/java.util.ServiceLoader$1.run(ServiceLoader.java:656)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.util.ServiceLoader.getConstructor(ServiceLoader.java:667)
... 48 more
Caused by: java.lang.ClassNotFoundException: org.keycloak.social.google.GoogleIdentityProviderConfig from [Module "deployment.mycompany-spi-ear.ear.mycompany-spi-ejb.jar" from Service Module Loader]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:255)
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
... 56 more
The interesting bit seeming to originate from GoogleIdentityproviderConfig.
I have the META-INF file setup as such:
resources/META-INF/services/org.keycloak.broker.social.SocialIdentityProviderFactory
com.mycompany.keycloak.useridmapper.GSuiteIdentityProviderFactory
GSuiteIdentityProviderFactory.java
package com.mycompany.keycloak.useridmapper;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.broker.social.SocialIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.social.google.GoogleIdentityProviderConfig;
public final class GSuiteIdentityProviderFactory extends AbstractIdentityProviderFactory<GSuiteIdentityProvider>
implements SocialIdentityProviderFactory<GSuiteIdentityProvider> {
public static final String PROVIDER_ID = "gsuite";
@Override
public String getName() {
return "GSuite";
}
@Override
public GSuiteIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
return new GSuiteIdentityProvider(session, new GoogleIdentityProviderConfig(model));
}
@Override
public GoogleIdentityProviderConfig createConfig() {
return new GoogleIdentityProviderConfig();
}
@Override
public String getId() {
return PROVIDER_ID;
}
}
I’m providing my own GSuiteIdentityProvider.java that simply extends from the GoogleIdentityProvider as I only want to augment the identity function.
package com.mycompany.keycloak.useridmapper;
import java.io.IOException;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.social.google.GoogleIdentityProvider;
import org.keycloak.social.google.GoogleIdentityProviderConfig;
public class GSuiteIdentityProvider extends GoogleIdentityProvider {
public GSuiteIdentityProvider(KeycloakSession session, GoogleIdentityProviderConfig config) {
super(session, config);
}
@Override
protected BrokeredIdentityContext extractIdentity(AccessTokenResponse tokenResponse, String accessToken,
JsonWebToken idToken) throws IOException {
BrokeredIdentityContext context = super.extractIdentity(tokenResponse, accessToken, idToken);
context.setFirstName("test-first-name");
context.setUserAttribute("foo", "bar");
return context;
}
}
Curious what I’m doing wrong here?
Longterm I wish to overwrite the doGetFederatedIdentity function in the provider similar to other social identity providers so I can make a call to the GSuite admin API to get the data I need. However, I’ve been unable to test anything due to the java errors when loading the SPI.
Note: I’m loading this as an EAR into a docker container where I drop it directly into the deployments directory. This works fine for my other plugins, but is failing specifically for this interface.