Deploy Camunda Identity with existing Keycloak

Hey :slight_smile:

I’m currently working on getting the Camunda Platform Helm Chart (without Web Modeller) up and running on AWS EKS. Camunda Identity should be connected with an existing internal Keycloak instance.

Nearly everything is working as expected, except Camunda Identity. I already configured KeyCloak as far as I understood the screenshots and provided the Client Secret via global.identity.keycloak.auth.existingSecretKey. Now I’m wondering if this makes sense at all and why the KeyCloak instance should also receive an admin user for the whole instance? There is no documentation why we should need this. In the deployment I can see the problematic environment variables are KEYCLOAK_SETUP_USER and KEYCLOAK_SETUP_PASSWORD. I assume this was only necessary for bundled KeyCloak installations which are not set up yet. Am I right?

This documentation says to set these variables accordingly. In the referenced documentation they say to set IDENTITY_CLIENT_SECRET and KEYCLOAK_REALM instead. But both variables aren’t set at all in the official Helm Chart. :frowning:

So this topic really lacks proper and clear documentation and makes it hard to get it working.

If I set IDENTITY_CLIENT_SECRET and KEYCLOAK_REALM, then it seems to connect. At least I get a lot more log output and some 200s. But then it stops with the following exception:

javax.ws.rs.ProcessingException: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "cryptoInfo" (class org.keycloak.representations.info.ServerInfoRepresentation), not marked as ignorable (14 known properties: "protocolMapperTypes", "providers", "identityProviders", "themes", "passwordPolicies", "clientInstallations", "memoryInfo", "enums", "socialProviders", "clientImporters", "profileInfo", "componentTypes", "systemInfo", "builtinProtocolMappers"])
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 1255] (through reference chain: org.keycloak.representations.info.ServerInfoRepresentation["cryptoInfo"])
        at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:245) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:88) ~[resteasy-jaxrs-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.specimpl.AbstractBuiltResponse.readEntity(AbstractBuiltResponse.java:256) ~[resteasy-jaxrs-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:125) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:62) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:151) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        at jdk.proxy2.$Proxy137.getInfo(Unknown Source) ~[?:?]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration.lambda$checkHealth$2(KeycloakConfiguration.java:95) ~[classes!/:?]
        at dev.failsafe.Functions.lambda$toCtxSupplier$9(Functions.java:228) ~[failsafe-3.3.1.jar!/:3.3.1]
        at dev.failsafe.Functions.lambda$get$0(Functions.java:46) ~[failsafe-3.3.1.jar!/:3.3.1]
        at dev.failsafe.internal.RetryPolicyExecutor.lambda$apply$0(RetryPolicyExecutor.java:74) ~[failsafe-3.3.1.jar!/:3.3.1]
        at dev.failsafe.SyncExecutionImpl.executeSync(SyncExecutionImpl.java:187) ~[failsafe-3.3.1.jar!/:3.3.1]
        at dev.failsafe.FailsafeExecutor.call(FailsafeExecutor.java:376) ~[failsafe-3.3.1.jar!/:3.3.1]
        at dev.failsafe.FailsafeExecutor.run(FailsafeExecutor.java:220) ~[failsafe-3.3.1.jar!/:3.3.1]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration.checkHealth(KeycloakConfiguration.java:95) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration.keycloakAdmin(KeycloakConfiguration.java:75) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration$$SpringCGLIB$$0.CGLIB$keycloakAdmin$2(<generated>) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration$$SpringCGLIB$$2.invoke(<generated>) ~[classes!/:?]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.0.7.jar!/:6.0.7]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-6.0.7.jar!/:6.0.7]
        at io.camunda.identity.impl.keycloak.config.KeycloakConfiguration$$SpringCGLIB$$0.keycloakAdmin(<generated>) ~[classes!/:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:645) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1417) ~[spring-beans-6.0.7.jar!/:6.0.7]
        at org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver$1.getTarget(ContextAnnotationAutowireCandidateResolver.java:107) ~[spring-context-6.0.7.jar!/:6.0.7]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:686) ~[spring-aop-6.0.7.jar!/:6.0.7]
        at org.keycloak.admin.client.Keycloak$$SpringCGLIB$$0.realm(<generated>) ~[keycloak-admin-client-19.0.3.jar!/:19.0.3]
        at io.camunda.identity.impl.keycloak.initializer.service.ScopeInitializationService.scopes(ScopeInitializationService.java:88) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.initializer.service.ScopeInitializationService.findScope(ScopeInitializationService.java:56) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.initializer.service.ScopeInitializationService.process(ScopeInitializationService.java:49) ~[classes!/:?]
        at java.util.ArrayList.forEach(Unknown Source) ~[?:?]
        at io.camunda.identity.impl.keycloak.initializer.service.ScopeInitializationService.process(ScopeInitializationService.java:45) ~[classes!/:?]
        at io.camunda.identity.impl.keycloak.initializer.KeycloakEnvironmentInitializer.run(KeycloakEnvironmentInitializer.java:64) ~[classes!/:?]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:760) ~[spring-boot-3.0.5.jar!/:3.0.5]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:750) ~[spring-boot-3.0.5.jar!/:3.0.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:317) ~[spring-boot-3.0.5.jar!/:3.0.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[spring-boot-3.0.5.jar!/:3.0.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[spring-boot-3.0.5.jar!/:3.0.5]
        at io.camunda.identity.Application.main(Application.java:18) ~[classes!/:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[identity.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:95) ~[identity.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[identity.jar:?]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[identity.jar:?]
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "cryptoInfo" (class org.keycloak.representations.info.ServerInfoRepresentation), not marked as ignorable (14 known properties: "protocolMapperTypes", "providers", "identityProviders", "themes", "passwordPolicies", "clientInstallations", "memoryInfo", "enums", "socialProviders", "clientImporters", "profileInfo", "componentTypes", "systemInfo", "builtinProtocolMappers"])
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 1255] (through reference chain: org.keycloak.representations.info.ServerInfoRepresentation["cryptoInfo"])
        at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1132) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2202) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1705) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1683) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:320) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:2079) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1229) ~[jackson-databind-2.14.2.jar!/:2.14.2]
        at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.readFrom(ResteasyJackson2Provider.java:194) ~[resteasy-jackson2-provider-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.readFrom(AbstractReaderInterceptorContext.java:66) ~[resteasy-jaxrs-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.core.interception.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:56) ~[resteasy-jaxrs-3.13.2.Final.jar!/:3.13.2.Final]
        at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:211) ~[resteasy-client-3.13.2.Final.jar!/:3.13.2.Final]
        ... 62 more

Hi @floricdev!

Firstly, I’m sorry to hear that our documentation is not clear in this situation - the documentation for Identity is something that we are actively looking at so this feedback is really useful.

Regarding your issue, could you confirm the version of Keycloak that Identity is trying to connect to?

Thanks,

Ben.

Hey Ben,

thanks for your fast response and the feedback regarding the documentation.

We are using KeyCloak 20.0.2 and the Helm Chart in version 8.2.0. Could it be possible that version 20 is not supported yet?

Hi @floricdev,

You’re welcome! Unfortunately that is the case at the moment, as per the supported environments section in the docs Identity can currently connect to Keycloak v16 and v19.

Whilst I am not able to provide an exact date or timeline I can say that adding support for Keycloak v20 is on our board.

1 Like

Ok, this good and also important to know :slight_smile:

This dependency on the KeyCloak version would create a high coupling for us between KeyCloak and Camunda versions, especially without secured timelines. I will discuss this internally and see if we will need to use the KeyCloak provided by the Helm Chart instead to reduce this dependency. :confused: Still it would lead to managing two KeyCloak instances instead of one and the one for Camunda might be outdated.

I have the very same problem when trying to integrate Camunda 8.25 with Keycloak 21.0: There are new attributes in the Keycloak API which result in an UnrecognizedPropertyException.
Ignoring unknown properties would certainly make Camunda 8 compatible with a broader range of new Keycloak versions. See also: https://www.baeldung.com/jackson-deserialize-json-unknown-properties

Error message when Camunda 8 connects with Keycloak 21.0:

javax.ws.rs.ProcessingException: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "cryptoInfo" (class org.keycloak.rep
resentations.info.ServerInfoRepresentation), not marked as ignorable (14 known properties: "protocolMapperTypes", "providers", "identityProviders", "themes", "passwordPolicies", "clientInstallations", "memoryInfo", "enums", "socialProviders", "clientI
mporters", "profileInfo", "componentTypes", "systemInfo", "builtinProtocolMappers"])
 at [Source: (org.jboss.resteasy.specimpl.AbstractBuiltResponse$InputStreamWrapper); line: 1, column: 1246] (through reference chain: org.keycloak.representations.info.ServerInfoRepresentation["cryptoInfo"])

Hi @martin_schaefer and @floricdev,

I have some good news! We have merged the changes required to support connecting to Keycloak 21 (whilst supporting current deployments) into the upcoming alpha release (8.3.0-alpha2) with the overall aim of releasing this with the 8.3 release.

Hope this helps,

Ben

2 Likes

This is very goond news. Is there already a snapshot of the upcoming alpha release which I could get my hands on?

Hi @martin_schaefer,

The snapshot that contains the change is this one Docker :slight_smile:

I would be interested to hear if you experience any issues!

Thanks,

Ben

1 Like

I’ve managed to integrate Camunda 8.3.0-alpha2 with an existing Keycloak 21.0 instance. Very nice! Thanks again for the hint.

Same for us! :slight_smile:

1 Like

Hi,
it is possible to add this fix to the camunda helm repo?

We now continued trying to connect our existing Keycloak instance instead of the one provided with the Helm chart.

Unfortunately we get the following exception, when trying to open Identity in the browser (resulting in a 500):

2023-07-05 14:53:10.427 ERROR 1 --- [nio-8080-exec-8] i.s.e.RestResponseEntityExceptionHandler : Unexpected error
java.lang.ClassCastException: class sun.security.ec.ECPublicKeyImpl cannot be cast to class java.security.interfaces.RSAPublicKey (sun.security.ec.ECPublicKeyImpl is in module jdk.crypto.ec of loader 'platform'; java.security.interfaces.RSAPublicKey is in module java.base of loader 'bootstrap')
	at io.camunda.identity.sdk.authentication.AbstractAuthentication.verify(AbstractAuthentication.java:140) ~[identity-sdk-8.0.0-SNAPSHOT.jar!/:?]
	at io.camunda.identity.sdk.authentication.AbstractAuthentication.verifyToken(AbstractAuthentication.java:126) ~[identity-sdk-8.0.0-SNAPSHOT.jar!/:?]
	at io.camunda.identity.sdk.authentication.AbstractAuthentication.verifyToken(AbstractAuthentication.java:103) ~[identity-sdk-8.0.0-SNAPSHOT.jar!/:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
	at java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
	at io.camunda.identity.sdk.annotation.AnnotationProcessor.lambda$apply$0(AnnotationProcessor.java:33) ~[identity-sdk-8.0.0-SNAPSHOT.jar!/:?]
	at jdk.proxy2.$Proxy173.verifyToken(Unknown Source) ~[?:?]
	at io.camunda.identity.security.spring.filter.JwtFilter.doFilterInternal(JwtFilter.java:72) ~[classes!/:?]

Are you aware of this bug?

I receive the same error in Operate, resulting in the “noPermission” screen:

2023-07-07 08:09:02.191 ERROR 7 --- [io-8080-exec-10] i.c.o.w.s.i.IdentityController : Error in authentication callback:
java.lang.ClassCastException: class sun.security.ec.ECPublicKeyImpl cannot be cast to class java.security.interfaces.RSAPublicKey (sun.security.ec.ECPublicKeyImpl is in module jdk.crypto.ec of loader 'platform'; java.security.interfaces.RSAPublicKey is in module java.base of loader 'bootstrap')
at io.camunda.identity.sdk.authentication.AbstractAuthentication.verify(AbstractAuthentication.java:140) ~[identity-sdk-8.2.7.jar!/:?]
at io.camunda.identity.sdk.authentication.AbstractAuthentication.verifyToken(AbstractAuthentication.java:126) ~[identity-sdk-8.2.7.jar!/:?]
at io.camunda.identity.sdk.authentication.AbstractAuthentication.verifyToken(AbstractAuthentication.java:103) ~[identity-sdk-8.2.7.jar!/:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
at io.camunda.identity.sdk.annotation.AnnotationProcessor.lambda$apply$0(AnnotationProcessor.java:33) ~[identity-sdk-8.2.7.jar!/:?]
at jdk.proxy2.$Proxy135.verifyToken(Unknown Source) ~[?:?]
at io.camunda.operate.webapp.security.identity.IdentityAuthentication.authenticate(IdentityAuthentication.java:139) ~[classes!/:?]
at io.camunda.operate.webapp.security.identity.IdentityService.getAuthenticationFor(IdentityService.java:86) ~[classes!/:?]
at io.camunda.operate.webapp.security.identity.IdentityController.loggedInCallback(IdentityController.java:81) ~[classes!/:?]

Might this be caused by some Keycloak configurations?

We found the reason and were able to fix it temporary:

using ES256 as the default signature algorithm leads to this error. So it seems like Identity only supports RSA right now. :frowning: Using RS256 in the Camunda realm for testing fixed it.

Hey @floricdev - sorry for the very late response on this (I didn’t receive a notification of messages for some reason).

The signature algorithm issue you experienced is known about and we have a piece of work in the pipeline to accommodate the additional algorithms Identity supports additional JWT signing algorithms · Issue #290 · camunda/issues · GitHub.