Code Monkey home page Code Monkey logo

camunda-platform-7-keycloak's Introduction

Camunda Platform 7 - Keycloak Identity Provider Plugin

Maven Central Apache License V.2

Keycloak

Keycloak™ (https://www.keycloak.org/) is an Open Source Identity and Access Management platform including advanced features such as User Federation, Identity Brokering and Social Login.

Camunda™ (https://camunda.com/) Platform 7 is perfectly suited to carry out BPM projects in the cloud. Identity management in the cloud, however, often differs from classical approaches. Camunda already provides a generic sample for Single Sign On when using Spring Boot. See https://github.com/camunda-consulting/code/tree/master/snippets/springboot-security-sso. Specific instructions on how to use Spring Boots OAuth2 SSO in combination with this Keycloak Identity Provider Plugin can be found below.

Why this plugin? SSO is sufficient in case you only want authentication but have no further advanced security roles. If one needs to use Camundas IdentityService APIs or wants to see actual Users and Groups show up in Cockpit, a custom IdentityProvider needs to be implemented as well.

This plugin provides the basis for using Keycloak as Identity Management solution and will provide a ReadOnlyIdentityProvider. What you will get is a fully integrated solution for using Keycloak as an Identity Provider in Camunda receiving users and groups from Keycloak. The authorization of these users and groups for Camunda resources itself remains within Camunda. This plugin allows the usage of Keycloak as Identity Provider even without SSO.

Beware: in case you want to use Keycloak's advanced login capabilities for social connections you must configure SSO as well. Password grant exchanges are only supported for Keycloak's internally managed users and users of an LDAP / Keberos User federation. Hence without SSO you will only be able to login with users managed by such connections.

Current version: 7.21.1
Latest tests with: Keycloak 24.0.3, 19.0.3-legacy, Camunda 7.21.0, 7.21.0-ee

Features

Changes in version 7.21.1

  • Upgrade to Camunda Platform 7.21.0
  • New configuration flag enforceSubgroupsInGroupQuery for enforcing subgroups in query results when using Keycloak >= 23.0.0

Changes in version 7.20.1

With version 7.20.0 Camunda Platform 7 switched to Spring Boot 3.1, JakartaEE 10 and a JDK 17 baseline. The Keycloak Identity Provider Plugin has been updated to support the new baseline versions of it's major dependencies.

  • Upgrade to Camunda Platform 7.20.0
  • Upgrade to Apache HttpComponents HttpClient 5
  • Upgrade to Spring Boot 3.1.x
  • Updated samples to Spring Security 6.1

Changes in version 7.19.0

  • Updated samples to Camunda Platform 7.19 and Keycloak 21.1

New in version 7.18.0

  • Fixed a bug for userId's containing a plus sign.
  • Updated samples to Camunda Platform 7.18 and Keycloak >= 18
  • Alternative for client side JWT authentication in Camunda Cockpit (incubation status)

Changes in Version 7.17.0

  • Renamed the extension from camunda-bpm-identity-keycloak to camunda-platform-7-keycloak
  • Updated samples to Camunda Platform 7.17
  • Introduced new version which reflects the Camunda Version used in samples and tests.

New in Version 2.2.3:

  • Optional Keycloak Login Cache - helps you to minimize password check requests to Keycloak and thus improve performance. Not applicable in SSO scenarios, but useful e.g. when using External Task Clients with Basic Auth.

New in Version 2.2.2:

  • Optimized user / group queries when using single items in userIdIn(...) / groupIdIn(...) selections

New in Version 2.2.1:

  • Fixed a bug where "like" filters in combination with missing Keycloak attributes (e.g. users without email) may cause a NullPointerException

New in Version 2.2.0:

  • Optional Keycloak Query Cache - helps you to minimize requests to Keycloak and thus improve performance.
  • Minor optimization of refresh token handling in case it is missing at all.

New in Version 2.1.0:

  • Auto retry with refreshed new token in case of Keycloak HTTP 401 responses (more stability in case of misconfigurations).

New in Version 2.0.0:

  • Support for Camunda Platform 7 Run
  • New options proxyUri, proxyUser, proxyPassword for optional proxy support.
  • Usage of com.google.code.gson for JSON (de)serialization.
  • Further internal refactorings and preparations for future enhancements.

New in Version 1.5.0:

  • New option maxResultSize for configuring the maximum result size of queries against the Keycloak REST API.

New in Version 1.4.0:

  • Corrected rare problems with group queries of a single user in case the Keycloak Client name is similar to this username and config property useUsernameAsCamundaUserId=true

New in Version 1.3.0:

  • Provided additional fat camunda-platform-7-keycloak-all.jar including transitive dependencies for easier installation e.g. on Apache Tomcat distribution with shared engine.

New in Version 1.2.0:

  • Optimized and correct searches in Keycloak mass data
  • Add missing paging functionality to queries

New in Version 1.1.0:

  • Ability to read group hierarchies.
  • New option useGroupPathAsCamundaGroupId for readable group IDs. Helps when configuring authorizations.

Version 1.0.0:

  • ReadOnlyIdentityProvider
  • Broad support for user and group queries
  • Compatible with Spring Boot OAuth2 SSO

Known limitations:

  • A strategy to distinguish SYSTEM and WORKFLOW groups is missing. Currently only the administrator group is mapped to type SYSTEM.
  • Some query filters are applied on the client side - the Keycloak REST API does not allow full criteria search in all required cases.
  • Sort criteria for queries are implemented on the client side - the Keycloak REST API does not allow result ordering.
  • Tenants are currently not supported.

Prerequisites in your Keycloak realm

  1. Keycloak docker images can be found on Keycloak Docker Hub.

  2. Create a new client named camunda-identity-service with access type confidential and service accounts enabled: IdentityServiceSettings Please be aware, that beginning with Keycloak 18, you do not only have to configure a valid redirect URL, but a valid post logout redirect URL as well. To keep things easy values can be the same.

  3. In order to use refresh tokens set the "Use Refresh Tokens For Client Credentials Grant" option within the "OpenID Connect Compatibility Modes" section (available in newer Keycloak versions):

    IdentityServiceOptions

  4. Add the roles query-groups, query-users, view-users to the service account client roles of your realm (choose realm-management or master-realm, depending on whether you are using a separate realm or master): IdentityServiceRoles

  5. Your client credentials can be found here: IdentityServiceCredentials

  6. Once you're done with the basic setup you're now ready to manage your users and groups with Keycloak. Please keep in mind, that in order to make the Keycloak Identity Provider work, you will need at least one dedicated Camunda admin group or Camunda admin user in your realm. Whether you create this group/user manually or import it using the LDAP user federation or any other Identity Provider is up to you. KeycloakGroups

Usage with Camunda Spring Boot

Maven Dependencies:

<dependency>
    <groupId>org.camunda.bpm.extension</groupId>
    <artifactId>camunda-platform-7-keycloak</artifactId>
    <version>7.21.0</version>
</dependency>

Add the following class to your Camunda Spring Boot application in order to activate the Keycloak Identity Provider Plugin:

package <your-package>;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.camunda.bpm.extension.keycloak.plugin.KeycloakIdentityProviderPlugin;

@Component
@ConfigurationProperties(prefix="plugin.identity.keycloak")
public class KeycloakIdentityProvider extends KeycloakIdentityProviderPlugin {
}

Configuration in application.yaml will then look as follows:

camunda.bpm:
  ...
  authorization:
    enabled: true

plugin.identity.keycloak:
  keycloakIssuerUrl: https://<your-keycloak-server>/auth/realms/<realm-name>
  keycloakAdminUrl: https://<your-keycloak-server>/auth/admin/realms/<realm-name>
  clientId: camunda-identity-service
  clientSecret: 42aa42bb-1234-4242-a24a-42a2b420cde0
  useEmailAsCamundaUserId: true
  administratorGroupName: camunda-admin

Hint: the engine must not create a user upon startup - the plugin is a ReadOnlyIdentityProvider. Hence you must not configure an admin-user for camunda.bpm in your application.yaml. The following configuration will likely cause errors upon startup:

camunda.bpm:
# DON'T DO THIS
  admin-user:
    id: demo
    password: demo
    firstName: Camunda

The admin-user part must be deleted in order to work properly. The recommended procedure for creating the admin user and admin group in Keycloak is to have the deployment pipeline do this during the environment setup phase.

A list of configuration options can be found below:

Property Description
keycloakIssuerUrl The basic issuer URL of your Keycloak server including the realm.
Sample for master realm: https://<your-keycloak-server>/auth/realms/master
keycloakAdminUrl The admin URL of the Keycloak server REST API including the realm.
Sample for master realm: https://<your-keycloak-server>/auth/admin/realms/master
clientId The Client ID of your application.
clientSecret The Client Secret of your application.
useEmailAsCamundaUserId Whether to use the Keycloak email attribute as Camunda's user ID. Default is false.

This is option is a fallback in case you don't use SSO and want to login using Camunda's web interface with your mail address and not the cryptic internal Keycloak ID. Keep in mind that you will only be able to login without SSO with Keycloak's internally managed users and users managed by the LDAP / Keberos User federation.
useUsernameAsCamundaUserId Whether to use the Keycloak username attribute as Camunda's user ID. Default is false. In the default case the plugin will use the internal Keycloak ID as Camunda's user ID.
useGroupPathAsCamundaGroupId Whether to use the Keycloak unique group path as Camunda's group ID. Default is false. In the default case the plugin will use the internal Keycloak ID as Camunda's group ID.
This flag is particularly useful in case you want to have human readable group IDs and recommended when using groups in Camunda's authorization management.
Since 1.1.0
enforceSubgroupsInGroupQuery Starting with Keycloak version 23 the group query without any other search parameters does not automatically return subgroups within the result. Set this flag to true in case you use subgroups together with Keycloak 23 or higher. Otherwise leave it to the default false and benefit from better performance.
Since 7.21.1
administratorGroupName The name of the administrator group. If this name is set and engine authorization is enabled, the plugin will create group-level Administrator authorizations on all built-in resources.
administratorUserId The ID of the administrator user. If this ID is set and engine authorization is enabled, the plugin will create user-level Administrator authorizations on all built-in resources.
authorizationCheckEnabled If this property is set to true, then authorization checks are performed when querying for users or groups. Otherwise authorization checks are not performed when querying for users or groups. Default: true.
Note: If you have a huge amount of Keycloak users or groups we advise to set this property to false to improve the performance of the user and group query.
maxResultSize Maximum result size of queries against the Keycloak API. Default: 250.

Beware: Setting the parameter to a too low value can lead to unexpected effects. Keep in mind that parts of the filtering takes place on the client side / within the plugin itself. Setting the parameter to a too high value can lead to performance and memory issues.
Since 1.5.0
maxHttpConnections Maximum number HTTP connections for the Keycloak connection pool. Default: 50
disableSSLCertificateValidation Whether to disable SSL certificate validation. Default: false. Useful in test environments.
proxyUri Optional URI of a proxy to use. Default: null, example: http://proxy:81.
Since 2.0.0
proxyUser Optional username for proxy authentication. Default: null.
Since 2.0.0
proxyPassword Optional password for proxy authentication. Default: null.
Since 2.0.0

Caching options

This is a ReadOnlyIdentityProvider which translates all queries against the Camunda IdentityService in REST queries against Keycloak. Under high load it makes sense to not request the same things again and again, especially since the data of users and groups do not change every second. Therefore this plugin provides an optional cache feature.

User and group query caching

In order to activate caching of user and group queries you have the following options available:

Property Description
cacheEnabled Enable caching of user and group queries to Keycloak to improve performance. Default: false.
Since 2.2.0
maxCacheSize Maximum size of the cache. Least used entries are evicted when this limit is reached. Default: 500.
Since 2.2.0
cacheExpirationTimeoutMin Time (in minutes) after which a cached entry is evicted. Default: 15 minutes.
Since 2.2.0

Besides caching of user and group queries there is another scenario where caching could make sense.

Login caching

Imagine a setup with lots of External Task Clients using HTTP Basic Auth against the Camunda REST API (e.g. set camunda.bpm.run.auth.enabled: true when using Camunda Run). Your External Task Clients then might trigger the IdentityProvider's checkPassword function at high frequency. This function requests a token from Keycloak each time it is called. In case of a successful response the login is treated as valid. High frequency then means requesting lots of tokens - in the worst case all for the same user and before an already delivered token has timed out. Therefore this plugin provides an optional login cache feature as well.

In order to activate the login cache you have the following options available:

Property Description
loginCacheEnabled Enable caching of login / check password requests to Keycloak to improve performance. Not applicable in case of SSO scenarios, but useful e.g. in case of External Tasks clients using HTTP Basic Auth only. Default: false
Since 2.2.3
loginCacheSize Maximum size of the login cache. Least used entries are evicted when this limit is reached. Default: 50.
Since 2.2.3
loginCacheExpirationTimeoutMin Time (in minutes) after which a login cache entry is evicted. Default: 15 minutes.
Since 2.2.3

On the downside this feature bypasses the password grant exchange function of Keycloak until the configured timeout expires. So the choice is yours. Please be aware that the login cache is not applicable for SSO scenarios.

Activating Single Sign On

In this part, we’ll discuss how to activate SSO – Single Sign On – for the Camunda Web App using Spring Boot and Spring Security 5.2.x OAuth 2.0 Client capabilities in combination with this plugin and Keycloak as authorization server.

In order to setup Spring Boot's OAuth2 security add the following Maven dependencies to your project:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

What we need is a bridge between Spring Security and Camunda. Hence insert a KeycloakAuthenticationProvider as follows:

/**
  * OAuth2 Authentication Provider for usage with Keycloak and KeycloakIdentityProviderPlugin. 
  */
public class KeycloakAuthenticationProvider extends ContainerBasedAuthenticationProvider {

    @Override
    public AuthenticationResult extractAuthenticatedUser(HttpServletRequest request, ProcessEngine engine) {

        // Extract user-name-attribute of the OAuth2 token
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication instanceof OAuth2AuthenticationToken) || !(authentication.getPrincipal() instanceof OidcUser)) {
            return AuthenticationResult.unsuccessful();
        }
        String userId = ((OidcUser)authentication.getPrincipal()).getName();
        if (StringUtils.isEmpty(userId)) {
            return AuthenticationResult.unsuccessful();
        }

        // Authentication successful
        AuthenticationResult authenticationResult = new AuthenticationResult(userId, true);
        authenticationResult.setGroups(getUserGroups(userId, engine));

        return authenticationResult;
    }

    private List<String> getUserGroups(String userId, ProcessEngine engine){
        List<String> groupIds = new ArrayList<>();
        // query groups using KeycloakIdentityProvider plugin
        engine.getIdentityService().createGroupQuery().groupMember(userId).list()
            .forEach( g -> groupIds.add(g.getId()));
        return groupIds;
    }

}

Last but not least add a security configuration and enable OAuth2 SSO:

/**
 * Camunda Web application SSO configuration for usage with KeycloakIdentityProviderPlugin.
 */
@ConditionalOnMissingClass("org.springframework.test.context.junit.jupiter.SpringExtension")
@EnableWebSecurity
@Configuration
public class WebAppSecurityConfig {

    @Bean
    @Order(2)
    public SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf
                        .ignoringRequestMatchers(antMatcher("/api/**"), antMatcher("/engine-rest/**")))
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers(
                                antMatcher("/assets/**"),
                                antMatcher("/app/**"),
                                antMatcher("/api/**"),
                                antMatcher("/lib/**"))
                        .authenticated()
                        .anyRequest()
                        .permitAll())
                .oauth2Login(withDefaults())
                .build();
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean containerBasedAuthenticationFilter(){

        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new ContainerBasedAuthenticationFilter());
        filterRegistration.setInitParameters(Collections.singletonMap("authentication-provider", "org.camunda.bpm.extension.keycloak.showcase.sso.KeycloakAuthenticationProvider"));
        filterRegistration.setOrder(201); // make sure the filter is registered after the Spring Security Filter Chain
        filterRegistration.addUrlPatterns("/app/*");
        return filterRegistration;
    }

    // The ForwardedHeaderFilter is required to correctly assemble the redirect URL for OAUth2 login. 
    // Without the filter, Spring generates an HTTP URL even though the container route is accessed through HTTPS.
    @Bean
    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
        FilterRegistrationBean<ForwardedHeaderFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ForwardedHeaderFilter());
        filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return filterRegistrationBean;
    }

    @Bean
    @Order(0)
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }
}

Finally configure Spring Security with your Keycloak Single Page Web App client-id and client-secret in application.yaml as follows:

# Spring Boot Security OAuth2 SSO
spring.security.oauth2:
  client:
    registration:
      keycloak:
        provider: keycloak
        client-id: camunda-identity-service
        client-secret: yyy2121abc21def2121ghi212132121abc21def2121ghi2121eyyy
        authorization-grant-type: authorization_code
        redirect-uri: "{baseUrl}/{action}/oauth2/code/{registrationId}"
        scope: openid, profile, email
    provider:
      keycloak:
        issuer-uri: https://<your-keycloak-server>/auth/realms/camunda
        authorization-uri: https://<your-keycloak-server>/auth/realms/camunda/protocol/openid-connect/auth
        user-info-uri: https://<your-keycloak-server>/auth/realms/camunda/protocol/openid-connect/userinfo
        token-uri: https://<your-keycloak-server>/auth/realms/camunda/protocol/openid-connect/token
        jwk-set-uri: https://<your-keycloak-server>/auth/realms/camunda/protocol/openid-connect/certs
        # set user-name-attribute one of: 
        # - sub                -> default; using keycloak ID as camunda user ID
        # - email              -> useEmailAsCamundaUserId=true
        # - preferred_username -> useUsernameAsCamundaUserId=true
        user-name-attribute: email

Beware: You have to set the parameter user-name-attribute of the spring.security.oauth2.client.provider.keycloak in a way that it matches the configuration of your KeycloakIdentityProviderPlugin:

  • useEmailAsCamundaUserId: true - set user-name-attribute: email
  • useUsernameAsCamundaUserId: true - set user-name-attribute: preferred_username
  • neither of the above two, using Keycloak's ID as default - set user-name-attribute: sub

Keep in mind that Keycloak's email attribute might not always be unique, depending on your setup. Email uniqueness can be configured on a per realm level depending on the setting Login with email.

Quickstart

As a quickstart into using and configuring the plugin we recommend to have a look at the Installation on Camunda Platform Run. You'll find a chapter "Docker Sample Setup" at the end of the README. This is a simple starting point.

If your intention is a complete SSO setup on Kubernetes you'll be more happy with the next reference.

Sample Spring Boot Project with SSO on Kubernetes

A sample project using this plugin including a basic SSO and Kubernetes setup can be found under Camunda Showcase for Spring Boot & Keycloak Identity Provider. See directory examples.

Installation on Apache Tomcat with Shared Process Engine

Even if from an architectural point of view Spring Boot is currently the most recommended approach for cloud scenarios, it is of course possible to install the plugin in other Camunda distributions as well. A description on how to install the plugin on an Apache Tomcat full distribution can be found under Installation on Tomcat. See directory examples.

Installation on Camunda Platform Run

A description on how to install the plugin on Camunda BPM Run can be found under Installation on Camunda BPM Run. See directory examples.

Installation on JBoss/Wildfly

A description on how to install the plugin on a JBoss/Wildfly can be found under Installation on JBoss/Wildfly. See directory examples.

Unit testing the plugin

In order to run the unit tests I have used a local docker setup of Keycloak with docker-compose.yml as follows:

version: "3.9"

services:
  jboss.keycloak:
    image: quay.io/keycloak/keycloak:24.0.3
    restart: unless-stopped
    environment:
      TZ: Europe/Berlin
      DB_VENDOR: h2
      KEYCLOAK_ADMIN: keycloak
      KEYCLOAK_ADMIN_PASSWORD: keycloak1!
      KC_HTTP_RELATIVE_PATH: /auth
    ports:
      - "8443:8443"
      - "8080:8080"
    command:
      - start-dev

For details see documentation on Running Keycloak in a container.

Maven test setup

Running unit tests from Maven requires configuring the details of a running Keycloak server. This can be achieved by setting the following environment variables:

Environment Variable Description
KEYCLOAK_URL Keycloak server URL.
Default value: http://localhost:8080/auth
KEYCLOAK_ADMIN_USER The admin user of the Keycloak server.
Default value: keycloak
KEYCLOAK_ADMIN_PASSWORD The admin password of the Keycloak server.
Default value: keycloak1!
KEYCLOAK_ENFORCE_SUBGROUPS_IN_GROUP_QUERY Wether to enforce subgroup results in group queries when testing with Keycloak >= 23.0.0
Default value: true

In case you choose Keycloak in the new Quarkus distribution, please be aware that /auth has been removed from the default context path. Hence, it is required to change the KEYCLOAK_URL for the tests. Tests run successfully against the Quarkus distribution, in case you start Keycloak in Development mode.


That's it. Have a happy Keycloak experience and focus on what really matters: the core processes of your customer.

Brought to you by:

Accso

Gunnar von der Beck, Accso - Accelerated Solutions GmbH


Resources

Maintainer

License

License: Apache License 2.0

camunda-platform-7-keycloak's People

Contributors

actions-user avatar cachescrubber avatar carsten-klaffke avatar chaima-mnsr avatar cmur2 avatar dependabot[bot] avatar domkun avatar fml2 avatar hassan-ghanem avatar ingorichtsmeier avatar juja0 avatar langleu avatar lwille avatar mf75 avatar miiit avatar renovate[bot] avatar stephenott avatar strangelookingnerd avatar thorbenlindhauer avatar vojkog avatar vonderbeck avatar yanavasileva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

camunda-platform-7-keycloak's Issues

Dockerfile error when building

I was trying to build the dockerfile and fixed the version issue but I get the following error near the end of the build:

--> COPY --from=JLINKED_JAVA /jlinked /opt/java/openjdk
--
  | error: build error: API error (500): {"message":"Error processing tar file(exit status 1): invalid symlink \"/opt/java/openjdk/legal/java.compiler/ADDITIONAL_LICENSE_INFO\" -> \"../java.base/ADDITIONAL_LICENSE_INFO\""}

Any suggestions?

Task List Performance with several hundred users

Hi,
I have a setup where Camunda 7.12 connects to Keycloak 9.0.2 which is connected to an LDAP server. On the LDAP more than 500 users and 100 groups are configured.
The access to the user list and managing user tasks has currently very bad performance. During my analysis I have seen, that many interactions query the whole list of users again. I can see a lot of requests to GET /auth/admin/realms/{realm}/users. This is e.g. cause by open user list, press the claim button in the task list, refresh the task list and many other interactions. As Keycloak needs several seconds to return the whole list every time, this makes Camunda task list not very usable at the moment.

I tried to play around with
plugin.identity.keycloak.authorization-check-enabled=false but could not see a big improvement.

Is there a possibility to cache the users - per session or even globally? Any other hint where to look or how to improve?

Is this plugin usable in camunda bpm run ?

I managed to get the plugin running in the camunda tomcat distribution by following the instructions

Now I want to do the same for bpm run
I copied the plugin (camunda-bpm-identity-keycloak-all-x.y.z.jar) to "configuration/userlib" as mentioned here (https://forum.camunda.org/t/plugins-for-camunda-bpm-run/20446)
and modified my application.yaml to include the same config that was used in the bpm-platform.xml for the tomcat distribution.
plugin.identity.keycloak: keycloakIssuerUrl: https://somehost:8443/auth/realms/camunda<realm-name> keycloakAdminUrl: https://somehost:8443/auth/admin/realms/camunda<realm-name> clientId: camunda-identity-service clientSecret: 42xx42xx-42xx-42xx-42xx-42xx42xx42xx useEmailAsCamundaUserId: true administratorGroupName: camunda-admin useGroupPathAsCamundaGroupId: true disableSSLCertificateValidation: true

In the log of camunda bpm run I see no evidence that the plugin is started, am I missing something ?

Official, easy to deploy: docker/docker-compose setup

Greetings!
I'm seeking for an easy to reproduce setup for a docker/docker-compose environment. As we still can't find an objective and direct solution to have the main product docker setup with SSO/Keyckoak yet.

I came across many topics, discussions and references on that matter to no avail. And yes, I read this beautiful guide from @VonDerBeck , that did shine a light at the end of the tunnel. but ultimatley I wasn't able to reproduce the expected results.

There's a really simple and promissing attempt on getting such setup running easily by Gabepurnam. But it doesn't seem quite ready yet. Nor pressing like another BPMN product that actually has an official image/instructions for a quick docker deploy with keycloak support enabled and installed by default.

Yet I still find Camunda to be the best candidate solution for our team, and I really wish we had such solution for a quick deployment of the system with Keycloak as simple as the default camunda docker image.

Are there any plans to get it available for us anytime soon?

Always got 403 at Camunda startup

Hi,

I'm not able to use your PlugIn. At startup of my application I got following exception:

Caused by: org.springframework.web.client.HttpClientErrorException$Forbidden: 403 Forbidden at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:83) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:123) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:785) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:586) ~[spring-web-5.2.1.RELEASE.jar:5.2.1.RELEASE] at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.getKeycloakAdminGroupId(KeycloakIdentityProviderSession.java:1043) ~[camunda-bpm-identity-keycloak-1.5.0.jar:1.5.0] ... 38 common frames omitted

I have an existing Keycloak server and added a new client and the admin group in my realm with all the settings out of the documentation. It seems that the login success but the request for the admin group fails.

Is there something to change in Keycloak what is missing in the documentation?

Best regards

Suggestion: More variables in the application.yaml

Would you mind updating the application.yaml to add additional variables?

Here is what I am thinking of:

  1. Allow the realm to be configurable
  2. Add a variable for clientID and ClientSecret

See below at the snipped.. I am happy to attempt a pull request if you agree...

keycloak.url.client: ${KEYCLOAK_URL_CLIENT:http://localhost:9000}
keycloak.url.token: ${KEYCLOAK_URL_TOKEN:http://localhost:9000}
keycloak.url.plugin: ${KEYCLOAK_URL_PLUGIN:https://localhost:9001}
keycloak.url.realm: ${KEYCLOAK_URL_REALM:master}
keycloak.clientId: ${KEYCLOAK_CLIENTID:camunda-identity-service}
keycloak.clientSecret: ${KEYCLOAK_CLIENTSECRET:7d3c845d-f652-4bed-9797-d6d20b7623da}

security:
oauth2:
client:
client-id: ${KEYCLOAK_CLIENTID}
client-secret: ${KEYCLOAK_CLIENTSECRET}
accessTokenUri: ${keycloak.url.token}/auth/realms/${KEYCLOAK_URL_REALM}/protocol/openid-connect/token
userAuthorizationUri: ${keycloak.url.client}/auth/realms/${KEYCLOAK_URL_REALM}/protocol/openid-connect/auth
scope: openid profile email
resource:
userInfoUri: ${keycloak.url.client}/auth/realms/${KEYCLOAK_URL_REALM}/protocol/openid-connect/userinfo

logging.level.org.springframework.security: DEBUG

plugin.identity.keycloak:
keycloakIssuerUrl: ${keycloak.url.plugin}/auth/realms/${KEYCLOAK_URL_REALM}
keycloakAdminUrl: ${keycloak.url.plugin}/auth/admin/realms/${KEYCLOAK_URL_REALM}
clientId: ${KEYCLOAK_CLIENTID}
clientSecret: ${KEYCLOAK_CLIENTSECRET}
useEmailAsCamundaUserId: true
useUsernameAsCamundaUserId: false
useGroupPathAsCamundaGroupId: true
administratorGroupName: camunda-admin
disableSSLCertificateValidation: true

Keycloak creates a session of camunda-identity-service client during spring boot application startup

Hi! First of all, thank you @VonDerBeck for such useful plugin. I need your help with session issue.
I have a camunda-identity-service client and another client with access type public for my Vuejs application. When I login to my Vuejs application with Keycloak login page, I am able to automatically login to Camunda web app because of SSO. Then, when I logout from Camunda web app, I am also automatically logout from Vuejs. All sessions destroy in Keycloak Session web page.
But if I logout within Vuejs application, Camunda web app remains active, because there is the active session in Keycloak Session web page, which was created when Spring boot application start up.
So, why camunda identity plugin creates a new session during Spring boot application starts up? How to solve the session issue?
Thank you in advance.

Group Hierarchy

HI,

Keycloak support group hierarchy i.e:

/London/Gatwick

As such London is the parent of Gatwick. If I was a member of the Group London then i would expect to see all the tasks for my child groups. Currently however if i am a member of /London and the task is assigned to /London/Gatwick then i do not see the task.

I think this needs updating where the check for candidate groups is.

Thanks

Mark

Keycloak group id retrieved only in bootstrap time

Hello,

I'm using keycloak plugin with admin-group-name parameter. And as I understood, in the bootstrap of service, plugin retrieve group id by name from keycloak and grant privileges in Camunda DB for that group ID.

This will work fine if group id will not be changed. So, actually binding is done by group id and not by group name.
We have tool in our system that load keycloak data (clients, groups and so on) and it can recreate group. So group name will be the same, but id will be changed. How we can deal with it?

Probably we can extend our tool to update authorization table in Camunda when new groups are uploaded, but this is not very desirable solution. Is there any possibility to make plugin to update group id periodically?

Does not work with Keycloak 8.0.1 version.

Hi,
I am using keycloak 8.0.1, camunda 7.11.0, spring-boot 2.2.1 and camunda-bpm-identity-keycloak 1.2.0. On application startup both API's group-by-path and groups gives 403 Forbidden error.

Profile and groups don't load when using preferred_username

Had someone tried to use preferred_username or sub as Camunda username? User can login and his username or sub is showed, but other info, like fullname, email and groups (camunda-admin, for example) did not appear.

I would be glad if someone could help. I'm importing users from another system to keycloak and want to use same username, not email.

Thanks a lot.

How to import roles as groups in the processEngine?

@VonDerBeck
Hi,
I have some users in my Keycloak that have no groups but roles. There is no way to give them groups, it has to stay like this.
Is there a way to import these users in the processEngine from Keycloak and having all their roles be mapped to groups?
In one discussion it sounded like it is not possible, because we had to wait for Keycloak version 11.0.0. But now this version is online and I am wondering if it would be possible now?
Is there a workaroung for now? For example at the startup of the application make a rest-call to Keycloak and get every user and their roles and then make some mapping from roles to groups and write them in the process-Engine?
Thank you for your help :)

realm name and proxy_uri configurable via docker-env

First of all: thanks a lot for this project. It helps a lot and works quite well!

I use the resulting docker image from your Dockerfile and needed to change the realm name in keycloak and to add my own nginx reverse proxy via docker environment.

Please have a look in to the application.yaml, attached.

application.yaml.zip

The keycloak.realm.id property defaults to "camunda" (as it was hard coded) and the plugin.identity.keycloak.proxyUri is taken from the env, too.

Maybe you can adapt this change into the code?

[Feature request] Allow to deactivate user queries

We plan to use the plugin for user authentication. We plan to manage the permissions on the group level only. I.e. the permissions will be granted only to groups, never to individual users.

We even plan to configure the keycloak server in a way that the following queries will not be allowed:

  • List members of a particular group
  • List all users

Only listing of groups will be allowed. Note that querying the data about a particular user (by her ID) will be allowed so that the plugin will be able to provide the membership info for the current user.

But if I understand correctly, for rendering of the pages in the admin area of the cockpit, the plugin will try to execute the above queries which will result in an error.

Would it be possible to add a config parameter that would mean "ignore user queries"? So that they always return empty lists (but no errors)?

I hope the motivation is clear. If not, I'll be happy to elaborate more. If it makes sense, I'd try to implement it.

Thank you for the very useful plugin!

Is it possible to do OAuth without Spring Boot?

I'm trying to provide SSO for an instance of Camuda via Google OAuth, and I'm reading various docs related to this plugin and I'm a bit confused. I keep seeing references to something called "Spring Boot", which appears to be some kind of external service as far as I can tell.

I have no interest in using this "Spring Boot" thing with my instance of Camunda, but all tutorials I've seen related to OAuth and Keycloak keep mentioning it. So my question is: Is it possible to do OAuth with Keycloak without Spring Boot?

I found this issue: #36
Which appears to describe my dilemma well, since I did install the Keycloak JAR and configured a default.yml file to use my already configured instance of Keycloak, but nothing has changed. I see no option to use Keycloak for login.

The official README file mentions this:

Add the following class to your Camunda Spring Boot application in order to activate the Keycloak Identity Provider Plugin:

And then provides some example Java code. Where is that supposed to go if I'm not using Spring Boot?

Camunda 7.13: Cockpit not redirecting to Keycloak

Hello, i've just upgraded my camunda engine to use 7.13 and noticed that logging into cockpit isn't redirecting to Keycloak SSO. The API is being protected correctly. I've been using the kubernetes example and last version i used of camunda was alpha-4. Anyone else noticed this?

Short lived cache for keycloak queries ?

We've been using this library on one of our projects and we notice that sometimes, the queries to keycloak tend to be a bottleneck. It's especially noticeable when someone clicks on 'claim' on tasklist which we were able to kindof solve by implementing a HAL cache but there are still several places which use the identity service directly to retrieve user/group info which we would like to cache as well.

We are now working on another project where we are still in the process of integrating this library and wanted to try and cache the queries to keycloak if possible.

We settled on an approach where we just have extended versions of KeycloakUserService and KeycloakGroupService which intercept the calls and return cached (short-lived.. 15 mins) versions if they exist.

Here's an excerpt of the relevant code

@OverRide
public List requestGroupsByUserId(KeycloakGroupQuery query)
{
Object key = CacheKeyGenerator.generate(query);
return this.cache.byUserId().get(key, () -> super.requestGroupsByUserId(query));
}

CacheKeyGenerator used in the snippet above generates a cache key given a query. For now, for each of the query types (user, group) we've handpicked fields from the queries to include in the cache key but that feels brittle as it can easily break if more fields are added/used in later versions. We were thinking of a couple of ways to tackle this and both would need enhancements to this library so wanted to have a discussion about this and check if PRs are welcome.

  1. Add overloaded versions of the methods requestUsersByGroupId and requestUsersWithoutGroupId in KeycloakUserService (and the corresponding ones in KeycloakGroupService) which take individual fields as arguments as opposed to the query itself. (The fields could be query filter fields, ordering properties and paging properties for example) so that extended versions can use those to calculate cache keys.
  2. Make KeycloakUserQuery and KeycloakGroupQuery implement equals and hashcode so that the queries themselves can act as cache keys.
  3. Implement caching on the RestTemplate used and not on KeycloakUserService and KeycloakGroupService.

Authorization using Basic Auth instead of Service Client Id and Secret on Keycloak 5.0.0

That version awaits the client_id and client_secret as user and password from the Basic Authorization header.

I managed to fix by changing this code to this:

    headers.add(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED + ";charset=" + keycloakConfiguration.getCharset());
    headers.add(HttpHeaders.AUTHORIZATION, "Basic "+java.util.Base64.getEncoder().encodeToString(
      (
        keycloakConfiguration.getClientId()
        +":"
        +keycloakConfiguration.getClientSecret()
      ).getBytes()
    ));
    HttpEntity<String> request = new HttpEntity<String>(
        "grant_type=client_credentials",
        headers);

Thank you!

Got an error when browser is opened to long

Hi at all,
When I am logged in in my camunda web app and I am not closing the browser for a longer time, then I get a 503 error.
I looked in the logs and it seems that the token is expired. To get rid of the error I need to restart the browser or open an incognito window. It seems that the token is stored in the browser cache.
Is there a way to get rid of this error?
Do you know some more details on this error?

Thanks a lot for your help :)

Shared Engine

Hi,
is there are any way that I can use this as plugin in a shared engine ?

Camunda + Keycloak Docker crash after started

Hi All,

I use tomcat example to deploy my camunda server as docker container

My Dockerfile is:


FROM camunda/camunda-bpm-platform

RUN rm -r webapps/camunda-invoice

COPY ./camunda-bpm-identity-keycloak-all-1.5.0.jar /camunda/lib/camunda-bpm-identity-keycloak-all-1.5.0.jar
COPY ./bpm-platform.xml /camunda/conf/bpm-platform.xml


I've inserted to bpm-platform.xml code example from my real KeyCloak server which setup by this instruction https://github.com/camunda/camunda-bpm-identity-keycloak

<plugins> ... <plugin> <class>org.camunda.bpm.extension.keycloak.plugin.KeycloakIdentityProviderPlugin</class> <properties> <property name="keycloakIssuerUrl">https://somehost:8443/auth/realms/camunda</property> <property name="keycloakAdminUrl">https://somehost:8443/auth/admin/realms/camunda</property> <property name="clientId">camunda-identity-service</property> <property name="clientSecret">42xx42xx-42xx-42xx-42xx-42xx42xx42xx</property> <property name="useUsernameAsCamundaUserId">true</property> <property name="useGroupPathAsCamundaGroupId">true</property> <property name="administratorGroupName">camunda-admin</property> <property name="disableSSLCertificateValidation">true</property> </properties> </plugin> ... </plugins>

camunda login still showing after logging in keycloak

Hello, I'm having trouble setting up camunda with keycloak, currently I followed two guides:

I copied the three main classes KeycloakAuthenticationProvider.java , KeycloakIdentityProvider and WebAppSecurityConfig that are mainly involved in configuring this with the exception of this line:
filterRegistration.setInitParameters(Collections.singletonMap("authentication-provider", "org.camunda.bpm.extension.keycloak.showcase.sso.KeycloakAuthenticationProvider")); I changed the second param to point to my current project directories so it won't error out.

Here is also my application.yml : https://pastebin.com/d6SJs6CD

For the keycloak settings I mainly followed the first guide. Only difference I made was I change the valid redirect uri to this: http://localhost:8081/* instead of http://localhost:8081/camunda/* if I changed it to the second one it results to a invalid redirect uri error.

I'm not sure what I'm doing wrong here, whenever I access the localhost:8081 (camunda) via browser it proceeds to keycloak's login once I entered the correct credentials it directs me to camunda's login.

Configure multiple clients in Camunda Keycloak plugin?

Hi at all,
I was wondering if it is possible to configure multiple clients in the Camunda Keycloak plugin.
For example:
I have an application that is able to map groups to roles.
I have one Keycloak client that contains some general roles that are used in multiple different applications.
And I have another Keycloak client that contains some specific roles that are used only for my application.
My question is now: Is it possible to configure the Camunda Keycloak plugin in a way, that all roles are called from the two different clients?

My answer would be no, because the Keycloak plugin is currently not supporting roles and for that reason, there is no need for such a feature.
Am I right or is there something I am missing?

Thanks a lot :)

Configuration file

Can we configure this plugin with a configuration file?

I'm creating as Docker Image following Wildfly installation but changing standalone.xml for this kind of settings doesn't fit ours needs, we need configure it more easily (that could be added or referred in docker-compose.yml) and must be in a file that only contains configurations for this plugin - standalone.xml doesn't follow this requirement.
Something like configuration file of camunda-bpm-mail extension.

Currently, I think this is not possible.
Does this feature make sense for your plugin? Is in backlog? If we develop this, are you interested in a PR?

Camunda and Keycloak's session issues

Hi all,

Firstly, I want to say thanks for the amazing plugin! It has benefited our solution a lot.

However, I have 3 questions to ask you about:

  1. In some scenarios, a user would be redirected back to the default camunda login page - and I do not want this to happen. An easy way to reproduce this is to configure the timeout duration of the tomcat server in camunda to be 60s. When camunda's session times out before keycloak's session does, the default camudna login page would show up. Is there a way to alter this redirection?

  2. If keycloak's session times out before camunda's session does... a white label error with 401 would show up (which makes sense), instead of keycloak's login screen. May I ask if there is a way to show keycloak's login screen instead of the error page?

  3. My team is doing session management separately on OpenShift. When we scale camunda from 2 pods down to 1 pod (for example), and then when I first login to camunda, I will get the white label error with 401. I need to wait about 10 minutes until I can successfully login again. I assume this has to do with sticky session - do you have any insights into this?

I would really appreciate it if you could help me out here... Thank you!!!

401 - Unauthorized after successful Keycloak Login

Environment

I tried setting up Keycloak with Camunda using the plugin described on this webpage.

application.yaml

server:
  port: 8082
  servlet:
    contextPath: /camunda
    session:
      cookie:
        name: OAUTH2SESSION

camunda.bpm:
  authorization:
    enabled: true

plugin.identity.keycloak:
  keycloakIssuerUrl: https://<domain>/auth/realms/<realm_id>
  keycloakAdminUrl: https://<domain>/auth/admin/realms/<realm_id>
  clientId: <client_id>
  clientSecret: <secret>
  useEmailAsCamundaUserId: true
  useGroupPathAsCamundaGroupId: true
  administratorGroupName: camunda-admin
  disableSSLCertificateValidation: true

security:
  disable-ssl: true
  oauth2:
    client:
      client-id: <client_id>
      client-secret: <secret>
      accessTokenUri: https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/token
      userAuthorizationUri: https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/auth
      scope: openid profile email
    resource:
      userInfoUri: https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/userinfo

Keycloak is running on a remote server and is already in use. The Keycloak instance uses Google as an Identity Provider. SSL is turned off in Keycloak, so requests can be received from local servers.

domain + token + realm_id have been redacted.

Issue

After successful authentication in Keycloak Google Login I get redirected to an error page "/error" with error message 401 Unauthorized.
After enabling debug logs for the application I was able to figure out the following:

13:19:45.366 [http-nio-8082-exec-4] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /app/; Attributes: [authenticated]
13:19:45.366 [http-nio-8082-exec-4] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@802887d1: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
13:19:45.368 [http-nio-8082-exec-4] DEBUG o.s.s.access.vote.AffirmativeBased - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@373f7e72, returned: -1
13:19:45.371 [http-nio-8082-exec-4] DEBUG o.s.b.a.audit.listener.AuditListener - AuditEvent [timestamp=2020-08-04T11:19:45.370Z, principal=anonymousUser, type=AUTHORIZATION_FAILURE, data={details=org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null, type=org.springframework.security.access.AccessDeniedException, message=Access is denied}]
13:19:45.374 [http-nio-8082-exec-4] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

and a few logs after:

13:19:58.698 [http-nio-8082-exec-6] DEBUG o.s.s.o.c.f.OAuth2ClientAuthenticationProcessingFilter - Request is to process authentication
13:19:58.728 [http-nio-8082-exec-6] DEBUG o.s.s.o.c.t.g.c.AuthorizationCodeAccessTokenProvider - Retrieving token from https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/token
13:19:58.738 [http-nio-8082-exec-6] DEBUG o.s.web.client.RestTemplate - HTTP POST https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/token
13:19:58.738 [http-nio-8082-exec-6] DEBUG o.s.s.o.c.t.g.c.AuthorizationCodeAccessTokenProvider - Encoding and sending form: {grant_type=[authorization_code], code=[996ffad1-0baf-43d1-b5b6-766109260d67.e60cb49d-482a-462c-acba-eb56f8a73743.7183196d-4d5b-47ef-b1a2-839c054a5e86], redirect_uri=[http://localhost:8082/camunda/login]}
13:19:58.926 [http-nio-8082-exec-6] DEBUG o.s.web.client.RestTemplate - Response 200 OK
13:19:58.949 [http-nio-8082-exec-6] DEBUG o.s.w.c.HttpMessageConverterExtractor - Reading to [org.springframework.security.oauth2.common.OAuth2AccessToken]
13:19:58.958 [http-nio-8082-exec-6] DEBUG o.s.b.a.s.o.r.UserInfoTokenServices - Getting user info from: https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/userinfo
13:19:58.960 [http-nio-8082-exec-6] DEBUG o.s.s.o.client.OAuth2RestTemplate - HTTP GET https://<domain>/auth/realms/<realm_id>/protocol/openid-connect/userinfo
13:19:58.961 [http-nio-8082-exec-6] DEBUG o.s.s.o.client.OAuth2RestTemplate - Accept=[application/json, application/*+json]
13:19:58.993 [http-nio-8082-exec-6] DEBUG o.s.s.o.client.OAuth2RestTemplate - Response 401 UNAUTHORIZED
13:19:58.999 [http-nio-8082-exec-6] DEBUG o.s.w.c.HttpMessageConverterExtractor - Reading to [org.springframework.security.oauth2.common.exceptions.OAuth2Exception]
13:19:59.001 [http-nio-8082-exec-6] WARN  o.s.b.a.s.o.r.UserInfoTokenServices - Could not fetch user details: class org.springframework.security.oauth2.common.exceptions.InvalidRequestException, Possible CSRF detected - state parameter was required but no state could be found
13:19:59.001 [http-nio-8082-exec-6] DEBUG o.s.b.a.s.o.r.UserInfoTokenServices - userinfo returned error: Could not fetch user details
13:19:59.003 [http-nio-8082-exec-6] DEBUG o.s.b.a.audit.listener.AuditListener - AuditEvent [timestamp=2020-08-04T11:19:59.003Z, principal=UNKNOWN, type=AUTHENTICATION_FAILURE, data={type=org.springframework.security.authentication.BadCredentialsException, message=Could not obtain user details from token}]
13:19:59.004 [http-nio-8082-exec-6] DEBUG o.s.s.o.c.f.OAuth2ClientAuthenticationProcessingFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Could not obtain user details from token
org.springframework.security.authentication.BadCredentialsException: Could not obtain user details from token
	at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:122)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:117)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:106)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.security.oauth2.common.exceptions.InvalidTokenException: <TOKEN>
	at org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices.loadAuthentication(UserInfoTokenServices.java:93)
	at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:112)
	... 57 common frames omitted

Processes are gone after restart in Docker

I have managed to follow the instructions given in https://github.com/camunda/camunda-bpm-identity-keycloak/tree/master/examples/run and https://github.com/camunda/camunda-bpm-identity-keycloak and deploy the 'Camunda BPM Run' onto Docker and run its service. I managed to login the camunda web with keycloak account with camunda-admin group. I have also deployed some processes to it and seen the processes on the camunda cockpit. But if I restart the container in Docker the processes will be gone.

I went to investigate the startup logs and found out there were issues regarding finding the hostname.
image
Complete log is as below:

JAVA_HOME is not set. Unexpected results may occur.,
Set JAVA_HOME to the directory of your local JDK to avoid this message.,
REST API enabled,
WebApps enabled,
classpath: ./internal/webapps/,./internal/rest/,./configuration/userlib/,./configuration/keystore/,
____ _ ____ ____ __ __ ____ ,
/ | __ _ _ __ ___ _ _ _ __ __| | __ _ | __ )| _ | / | | _ \ _ _ _ __ ,
| | / | '_ _ | | | | ' \ / |/ _ | | _ | |) | |/| | | |
) | | | | '
\ ,
| |
| (| | | | | | | || | | | | (| | (| | | |) | __/| | | | | _ <| || | | | |,
_/_,|| || | _,|| | _,|_,| |/|| || || || _\__,|| |_|,
,
Spring-Boot: (v2.3.1.RELEASE),
Camunda BPM: (v7.15.0-alpha1),
,
2021-02-09 07:12:37.194 INFO 8 --- [ main] org.camunda.bpm.run.CamundaBpmRun : Starting CamundaBpmRun v7.15.0-alpha1 with PID 8 (/internal/camunda-bpm-run-core.jar started by root in /),
2021-02-09 07:12:37.198 INFO 8 --- [ main] org.camunda.bpm.run.CamundaBpmRun : No active profile set, falling back to default profiles: default,
2021-02-09 07:12:39.038 INFO 8 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http),
2021-02-09 07:12:39.057 INFO 8 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat],
2021-02-09 07:12:39.058 INFO 8 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.36],
2021-02-09 07:12:39.154 INFO 8 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext,
2021-02-09 07:12:39.154 INFO 8 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1898 ms,
2021-02-09 07:12:39.204 INFO 8 --- [ main] .c.b.s.b.s.r.CamundaJerseyResourceConfig : Configuring camunda rest api.,
2021-02-09 07:12:39.238 INFO 8 --- [ main] .c.b.s.b.s.r.CamundaJerseyResourceConfig : Finished configuring camunda rest api.,
2021-02-09 07:12:39.685 INFO 8 --- [ main] org.camunda.bpm.spring.boot : STARTER-SB040 Setting up jobExecutor with corePoolSize=3, maxPoolSize:10,
2021-02-09 07:12:39.688 INFO 8 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'camundaTaskExecutor',
2021-02-09 07:12:39.791 INFO 8 --- [ main] org.camunda.bpm.engine.cfg : ENGINE-12003 Plugin 'CompositeProcessEnginePlugin[genericPropertiesConfiguration, camundaDeploymentConfiguration, camundaProcessEngineConfiguration, camundaDatasourceConfiguration, camundaJobConfiguration, camundaHistoryConfiguration, camundaMetricsConfiguration, camundaAuthorizationConfiguration, failedJobConfiguration, eventPublisherPlugin, SpringBootSpinProcessEnginePlugin, org.camunda.bpm.extension.keycloak.run.plugin.KeycloakIdentityProvider$$EnhancerBySpringCGLIB$$244eb050@11ee02f8]' activated on process engine 'default',
2021-02-09 07:12:39.798 INFO 8 --- [ main] org.camunda.bpm.spring.boot : STARTER-SB021 Auto-Deploying resources: [],
2021-02-09 07:12:39.805 INFO 8 --- [ main] o.c.b.s.b.s.event.EventPublisherPlugin : EVENTING-001: Initialized Camunda Spring Boot Eventing Engine Plugin.,
2021-02-09 07:12:39.806 INFO 8 --- [ main] o.c.b.s.b.s.event.EventPublisherPlugin : EVENTING-003: Task events will be published as Spring Events.,
2021-02-09 07:12:39.806 INFO 8 --- [ main] o.c.b.s.b.s.event.EventPublisherPlugin : EVENTING-005: Execution events will be published as Spring Events.,
2021-02-09 07:12:39.809 INFO 8 --- [ main] o.c.b.s.b.s.event.EventPublisherPlugin : EVENTING-007: History events will be published as Spring events.,
2021-02-09 07:12:39.816 INFO 8 --- [ main] org.camunda.spin : SPIN-01010 Discovered Spin data format provider: org.camunda.spin.impl.json.jackson.format.JacksonJsonDataFormatProvider[name = application/json],
2021-02-09 07:12:40.054 INFO 8 --- [ main] org.camunda.spin : SPIN-01010 Discovered Spin data format provider: org.camunda.spin.impl.xml.dom.format.DomXmlDataFormatProvider[name = application/xml],
2021-02-09 07:12:40.066 INFO 8 --- [ main] org.camunda.spin : SPIN-01009 Discovered Spin data format: org.camunda.spin.impl.xml.dom.format.DomXmlDataFormat[name = application/xml],
2021-02-09 07:12:40.066 INFO 8 --- [ main] org.camunda.spin : SPIN-01009 Discovered Spin data format: org.camunda.spin.impl.json.jackson.format.JacksonJsonDataFormat[name = application/json],
2021-02-09 07:12:40.435 INFO 8 --- [ main] org.camunda.bpm.extension.keycloak : KEYCLOAK-01001 PLUGIN KeycloakIdentityProvider$$EnhancerBySpringCGLIB$$244eb050 activated on process engine default,
2021-02-09 07:12:40.588 INFO 8 --- [ main] org.camunda.bpm.dmn.feel.scala : FEEL/SCALA-01001 Spin value mapper detected,
2021-02-09 07:12:40.736 INFO 8 --- [ main] org.camunda.feel.FeelEngine : Engine created. [value-mapper: CompositeValueMapper(List(org.camunda.feel.impl.JavaValueMapper@76f856a8, org.camunda.spin.plugin.impl.feel.integration.SpinValueMapper@7c853486)), function-provider: org.camunda.bpm.dmn.feel.impl.scala.function.CustomFunctionTransformer@4d6ee47, clock: SystemClock, configuration: Configuration(false)],
2021-02-09 07:12:40.850 INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...,
2021-02-09 07:12:41.133 INFO 8 --- [ main] com.zaxxer.hikari.pool.PoolBase : HikariPool-1 - Driver does not support get/set network timeout for connections. (Receiver class org.h2.jdbc.JdbcConnection does not define or inherit an implementation of the resolved method 'abstract int getNetworkTimeout()' of interface java.sql.Connection.),
2021-02-09 07:12:41.141 INFO 8 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.,
2021-02-09 07:12:44.944 WARN 8 --- [ main] org.camunda.bpm.engine : ENGINE-00009 Could not determine local IP address for generating a host name,
,
java.net.UnknownHostException: d71e7fc01648: d71e7fc01648: Name does not resolve,
at java.base/java.net.InetAddress.getLocalHost(InetAddress.java:1642) ~[na:na],
at org.camunda.bpm.engine.impl.history.event.SimpleIpBasedProvider.getHostname(SimpleIpBasedProvider.java:36) ~[camunda-engine-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl.initHostName(ProcessEngineConfigurationImpl.java:2308) ~[camunda-engine-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl.init(ProcessEngineConfigurationImpl.java:1031) ~[camunda-engine-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl.buildProcessEngine(ProcessEngineConfigurationImpl.java:972) ~[camunda-engine-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.spring.SpringTransactionsProcessEngineConfiguration.buildProcessEngine(SpringTransactionsProcessEngineConfiguration.java:67) ~[camunda-engine-spring-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.spring.ProcessEngineFactoryBean.getObject(ProcessEngineFactoryBean.java:55) ~[camunda-engine-spring-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.camunda.bpm.engine.spring.ProcessEngineFactoryBean.getObject(ProcessEngineFactoryBean.java:34) ~[camunda-engine-spring-7.15.0-alpha1.jar!/:7.15.0-alpha1],
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:101) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1821) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory.java:1266) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:260) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1529) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1486) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1267) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1224) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:893) ~[spring-beans-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) ~[spring-context-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551) ~[spring-context-5.2.7.RELEASE.jar!/:5.2.7.RELEASE],
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.1.RELEASE.jar!/:2.3.1.RELEASE],
at org.camunda.bpm.run.CamundaBpmRun.main(CamundaBpmRun.java:25) ~[classes!/:7.15.0-alpha1],
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na],
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na],
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na],
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na],
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[camunda-bpm-run-core.jar:7.15.0-alpha1],
at org.springframework.boot.loader.Launcher.launch(Launcher.java:109) ~[camunda-bpm-run-core.jar:7.15.0-alpha1],
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[camunda-bpm-run-core.jar:7.15.0-alpha1],
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:466) ~[camunda-bpm-run-core.jar:7.15.0-alpha1],
Caused by: java.net.UnknownHostException: d71e7fc01648: Name does not resolve,
at java.base/java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method) ~[na:na],
at java.base/java.net.InetAddress$PlatformNameService.lookupAllHostAddr(InetAddress.java:929) ~[na:na],
at java.base/java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1515) ~[na:na],
at java.base/java.net.InetAddress$NameServiceAddresses.get(InetAddress.java:848) ~[na:na],
at java.base/java.net.InetAddress.getAllByName0(InetAddress.java:1505) ~[na:na],
at java.base/java.net.InetAddress.getLocalHost(InetAddress.java:1637) ~[na:na],
... 47 common frames omitted,
,
2021-02-09 07:12:45.054 INFO 8 --- [ main] org.camunda.bpm.connect : CNCT-01004 Discovered provider for connector id 'http-connector' and class 'org.camunda.connect.httpclient.impl.HttpConnectorImpl': 'org.camunda.connect.httpclient.impl.HttpConnectorProviderImpl',
2021-02-09 07:12:45.058 INFO 8 --- [ main] org.camunda.bpm.connect : CNCT-01004 Discovered provider for connector id 'soap-http-connector' and class 'org.camunda.connect.httpclient.soap.impl.SoapHttpConnectorImpl': 'org.camunda.connect.httpclient.soap.impl.SoapHttpConnectorProviderImpl',
2021-02-09 07:12:45.209 INFO 8 --- [ main] org.camunda.bpm.engine : ENGINE-00001 Process Engine default created.,
2021-02-09 07:12:46.018 INFO 8 --- [ main] o.c.b.s.b.s.w.f.LazyInitRegistration : lazy initialized org.camunda.bpm.spring.boot.starter.webapp.filter.LazySecurityFilter@4c4d362a,
2021-02-09 07:12:46.020 INFO 8 --- [ main] o.c.b.s.b.s.w.f.LazyInitRegistration : lazy initialized org.camunda.bpm.spring.boot.starter.webapp.filter.LazyProcessEnginesFilter@76318a7d,
2021-02-09 07:12:46.112 INFO 8 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '',
2021-02-09 07:12:46.128 INFO 8 --- [ main] org.camunda.bpm.run.CamundaBpmRun : Started CamundaBpmRun in 9.694 seconds (JVM running for 10.449),
2021-02-09 07:12:46.130 INFO 8 --- [ main] org.camunda.bpm.engine.jobexecutor : ENGINE-14014 Starting up the JobExecutor[org.camunda.bpm.engine.spring.components.jobexecutor.SpringJobExecutor].,
2021-02-09 07:12:46.135 INFO 8 --- [ingJobExecutor]] org.camunda.bpm.engine.jobexecutor : ENGINE-14018 JobExecutor[org.camunda.bpm.engine.spring.components.jobexecutor.SpringJobExecutor] starting to acquire jobs,

I suspect is the database that does not store the data but I am not sure. I run it in localhost there is no issue. Any suggestions or solution to this?

Camunda can’t take new token from Keycloak. Unable to query users

I have configured extension - camunda-bpm-identity-keycloak within K8s cluster.
It works well, but sometimes camunda can’t take new token when previous has expired. (Or keycloak doesn’t give new one).
Temporary solution - delete pod with camunda and deployment will create new one. After this camunda works well.
Below you can find configuration and logs.

Camunda pod logs:
13-May-2021 09:53:41.309 WARNING [http-nio-8080-exec-3] org.camunda.commons.logging.BaseLogger.logWarn ENGINE-REST-HTTP500 org.camunda.bpm.engine.impl.identity.IdentityProviderException: Unable to query users at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.requestUsersWithoutGroupId(KeycloakIdentityProviderSession.java:314) at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.findUserByQueryCriteria(KeycloakIdentityProviderSession.java:138) at org.camunda.bpm.extension.keycloak.KeycloakUserQuery.executeList(KeycloakUserQuery.java:36) at org.camunda.bpm.engine.impl.AbstractQuery.evaluateExpressionsAndExecuteList(AbstractQuery.java:216) at org.camunda.bpm.engine.impl.AbstractQuery.executeSingleResult(AbstractQuery.java:238) at org.camunda.bpm.engine.impl.AbstractQuery.execute(AbstractQuery.java:194) at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:28) at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:110) at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70) at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33) at org.camunda.bpm.engine.impl.AbstractQuery.executeResult(AbstractQuery.java:159) at org.camunda.bpm.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:135) at org.camunda.bpm.webapp.impl.security.auth.AuthenticationService.createAuthenticate(AuthenticationService.java:63) at org.camunda.bpm.webapp.impl.security.auth.UserAuthenticationResource.doLogin(UserAuthenticationResource.java:95) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:526) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:415) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:376) at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:356) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:378) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:347) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:320) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135) at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:356) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215) at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.engine.rest.filter.CacheControlFilter.doFilter(CacheControlFilter.java:45) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.engine.rest.filter.EmptyBodyFilter.doFilter(EmptyBodyFilter.java:101) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.webapp.impl.security.filter.headersec.HttpHeaderSecurityFilter.doFilter(HttpHeaderSecurityFilter.java:87) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.webapp.impl.security.filter.CsrfPreventionFilter.doFilter(CsrfPreventionFilter.java:175) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.webapp.impl.security.filter.SecurityFilter.doFilterSecure(SecurityFilter.java:71) at org.camunda.bpm.webapp.impl.security.filter.SecurityFilter.doFilter(SecurityFilter.java:55) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter$1.execute(AuthenticationFilter.java:62) at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter$1.execute(AuthenticationFilter.java:60) at org.camunda.bpm.webapp.impl.security.SecurityActions.runWithAuthentications(SecurityActions.java:44) at org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter.doFilter(AuthenticationFilter.java:60) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:678) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: keycloakjar.org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized at keycloakjar.org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:81) at keycloakjar.org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:123) at keycloakjar.org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102) at keycloakjar.org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) at keycloakjar.org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:785) at keycloakjar.org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743) at keycloakjar.org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677) at keycloakjar.org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:586) at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.requestUserById(KeycloakIdentityProviderSession.java:389) at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.requestUsersWithoutGroupId(KeycloakIdentityProviderSession.java:262)

Login behaivor:
image

camunda-identity-service:
image

Thank you in advance for response. Let me know if I need to share more info about my settings.

ClassCastException with custom Keycloak Authentication Filter

Hello,

I already asked this question in this post of the Camunda Forum but received no answer.

I am writing my own Container Based Authentication Filter. I wrote the following java class KeycloakSSOAuthenticationProvider that implements AuthenticationProvider and looks something like this:

package com.somename.sso.camunda;
import org.camunda.bpm.engine.rest.security.auth.AuthenticationProvider;

public class KeycloakSSOAuthenticationProvider implements AuthenticationProvider {

    @Override
    public AuthenticationResult extractAuthenticatedUser(final HttpServletRequest request, final ProcessEngine engine) {
        // Does something
        return authenticationResult;
    }

    private List<String> getUserGroups(final String userId, final ProcessEngine engine) {
        // Does something
        return groupIds;
    }

    public void augmentResponseByAuthenticationChallenge(final HttpServletResponse response,
            final ProcessEngine engine) {
        ;
    }
}

Here ist the filter description from my web.xml file:

<filter>
    <filter-name>Container Based Authentication Filter</filter-name>
    <filter-class>org.camunda.bpm.webapp.impl.security.auth.ContainerBasedAuthenticationFilter</filter-class>
    <init-param>
      <param-name>authentication-provider</param-name>
      <param-value>com.somename.sso.camunda.KeycloakSSOAuthenticationProvider</param-value>
      <!-- <param-value>org.camunda.community.auth.keycloak.sso.KeycloakSSOAuthenticationProvider</param-value> -->
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>Container Based Authentication Filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

There is an error at runtime when the filter tries to cast KeycloakSSOAuthenticationProvider to AuthenticationProvider. I would suppose that this casting should not be a problem since the first one implements the second one.

However it appears that KeycloakSSOAuthenticationProvider is loaded by URLClassLoader, while AuthenticationProvider is loaded by ParallelWebappClassLoader and this is creating the error. Here the error log I get when running camunda:

Caused by: java.lang.ClassCastException: class com.somename.sso.camunda.KeycloakSSOAuthenticationProvider cannot be cast to class org.camunda.bpm.engine.rest.security.auth.AuthenticationProvider (com.somename.sso.camunda.KeycloakSSOAuthenticationProvider is in unnamed module of loader java.net.URLClassLoader @6fc6f14e; org.camunda.bpm.engine.rest.security.auth.AuthenticationProvider is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader @7a498efa)

How should I approach this issue?

Roles <-> Groups

Hi,

as far as I understand it, groups are just for managing users in Keycloak whereas roles are for managing permissions. (Group people in Groups, grant different groups different sets of roles).

But on the other hand you can have nested groups but not nested permissions in keycloak. Nested groups would be very handy to not map all keycloak groups into camunda but just the relevant ones.

As you see I don't have a settled opinion on this but I think it's worth discussing.

Best wishes,
Adrian Schneider

7.12 support

I have 7.12 running with this. I hope I did it right.

I made more changes to my fork (SSO authentication for the engine, support for groovy, http connectors) but would you like a pull request?

Updates I made In pom-docker.xml:

<version.camunda>7.12.0</version.camunda>
<version.camundaSpringBoot>3.4.0</version.camundaSpringBoot>
<version.springBoot>2.2.1.RELEASE</version.springBoot>
<version.springSecurityOauth2>2.2.1.RELEASE</version.springSecurityOauth2>

<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.190</version>

Thanks for the great sample app!!!

401 - Unauthorized after successfull login

Hello :)

I have setup my Camunda project with Keycloak. It is working fine, but if I want to secure my Rest-Api I am getting an error. When I am trying to access the "/engine-rest/**" path, I am getting the following error:

[2020-09-07T14:33:11.011Z] [org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor] [http-nio-8080-exec-8] [61] [DEBUG] Token not found in request parameters.  Not an OAuth2 request.
[2020-09-07T14:33:11.011Z] [org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter] [http-nio-8080-exec-8] [145] [DEBUG] No token in request, will continue chain.
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 7 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 8 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.authentication.AnonymousAuthenticationFilter] [http-nio-8080-exec-8] [100] [DEBUG] Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@51722761: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: A0D442959B5FC57BDDD6D37CDE20E28B; Granted Authorities: ROLE_ANONYMOUS'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.FilterChainProxy$VirtualFilterChain] [http-nio-8080-exec-8] [328] [DEBUG] /engine-rest/engine at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
[2020-09-07T14:33:11.011Z] [org.springframework.security.access.intercept.AbstractSecurityInterceptor] [http-nio-8080-exec-8] [219] [DEBUG] Secure object: FilterInvocation: URL: /engine-rest/engine; Attributes: [#oauth2.throwOnError(authenticated)]
[2020-09-07T14:33:11.011Z] [org.springframework.security.access.intercept.AbstractSecurityInterceptor] [http-nio-8080-exec-8] [348] [DEBUG] Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@51722761: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@2cd90: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: A0D442959B5FC57BDDD6D37CDE20E28B; Granted Authorities: ROLE_ANONYMOUS
[2020-09-07T14:33:11.011Z] [org.springframework.security.access.vote.AffirmativeBased] [http-nio-8080-exec-8] [66] [DEBUG] Voter: org.springframework.security.web.access.expression.WebExpressionVoter@27571df3, returned: -1
[2020-09-07T14:33:11.011Z] [org.springframework.security.web.access.ExceptionTranslationFilter] [http-nio-8080-exec-8] [180] [DEBUG] **Access is denied (user is anonymous);** redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:123)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:118)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:158)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:180)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:64)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:834)

It seems that the granted role is only anonymous and that is why the user cannot access the rest-api. But I have no idea to change this, could you please help me?

My Application.yml looks exactly like the showcase and I configured my keycloak server exactly as described in the showcase.

Thanks in advance :)

When will version 2.0.0 be released?

Hi @VonDerBeck ,
I was wondering when version 2.0.0 will be released?
Some http problems brought me to this question, because in the version 1.5.0 of this keycloak-plugin, there is no http support and I saw that in version 2.0.0 the http support is implemented.
For now I have to build my own Camunda-Keycloak plugin where I use the code of version 2.0.0, but this is pretty unclean and I would like to use the version 2.0.0 direclty from here.

Is there any date when this version could be released?

Thank you very much for your help 👍

401 after token expiry

Hi there

I've noticed that I get the following stack trace:

on

org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [{"error":"HTTP 401 Unauthorized"}]
	at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:105)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:170)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:112)
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:782)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:740)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583)
	at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.getKeycloakUserID(KeycloakIdentityProviderSession.java:424)

Restarting the engine fixes the issue. It seems to be once the token has expired. I've used the example sso-kubernetes.

Any help would be appreciated.

There is no Keycloak login window

Hi at all,
I tried to implement the Camunda Keycloak plugin in my Springboot application. I started my own Keycloak server and configured at as mentioned in the description.
However when I start the application, there is no Keycloak login window showing up. There is still the camunda login page.
Does anybody know why this could be?
I tripple checked now, I followed exactly the instructions from the plugin site...

I am thankful for each hint 👍

Logout doesn't work

Hi there!

Thanks for this package! Really useful.
However -- how can I handle logout? I tried different approaches, but whatever I do, the client get's logged in again. Could you provide me a path? I'm not firm in Spring Security.

Best wishes
Adrian Schneider

oath2 Redirect URI

I tried the oaut2 authentication with kk and i have problem when I deploy the setup into OpenShift clouds orchestrator. Locally all work well in http. When I deploy, internally into OpenShift it still in http and I create a route that expose https hostname into docker container exposed port 8080. the problem is that the redirection is done ever in http. Perhaps is an spring oaut2 configuration to achieve?

thanks in advance

Individual commits of a release not visible

Hello,

when I go to the releases page of the project, I can't see the individual commits that have been made since the last release. Thus I can't see the exact list of the features of the new release. This is very inconvenient.

Could you please fix that?

Rest API uses email address as id but username

I'm trying to integrate a camunda spring boot app with keycloak. Thanks a lot for your great works and detail documents/samples, it works fine. However, when using Rest API, I got such error(in the case, username is admin and its email is [email protected]):

{
    "type": "AuthorizationException",
    "message": "The user with id '[email protected]' does not have 'CREATE' permission on resource 'Deployment'.",
    "userId": "[email protected]",
    "resourceName": "Deployment",
    "resourceId": null,
    "permissionName": "CREATE",
    "missingAuthorizations": [
        {
            "permissionName": "CREATE",
            "resourceName": "Deployment",
            "resourceId": null
        }
    ]
}

I've set useEmailAsCamundaUserId to false and useUsernameAsCamundaUserId to true under plugin.identity.keycloak. I can also login to the webapps with admin but [email protected].

In order to access the Rest API, I acquired a token via /auth/realms/<myrealm>/protocol/openid-connect/token of keycloak server with required form data(client_id, username and so on), and using the returned access_token as header of Authorization: Bearer <access_token>, I got the error info.

Could you help me to know if there is something missed in my app please?

Session Idle Expiration Issue

If Keycloak is configured to expire tokens on session idle (see documentation) then if session goes idle the client token stored in the KeycloakContext will expire before the known token expiration date. The means any attempt to make REST calls to Keycloak with this expired token will fail until the token's expiration date.

Since there is no way for a client to know if it's session expired because of idle, the client should attempt to refresh the token once when a REST call to Keycloak fails with a 401 status.

kk Auth error from plugin

Hi, following instruction is very simple to configure all and camunda work very well, but after the token expires I go this error below from:

KeycloakIdentityProviderSession.getKeycloakUserID

Can be the plugin configure to do an auto refresh?

`
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized

  | at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:81) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579) ~[spring-web-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.getKeycloakUserID(KeycloakIdentityProviderSession.java:423) ~[camunda-bpm-identity-keycloak-1.2.0.jar!/:1.2.0]
  | at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.requestGroupsByUserId(KeycloakIdentityProviderSession.java:739) ~[camunda-bpm-identity-keycloak-1.2.0.jar!/:1.2.0]
  | at org.camunda.bpm.extension.keycloak.KeycloakIdentityProviderSession.findGroupByQueryCriteria(KeycloakIdentityProviderSession.java:713) ~[camunda-bpm-identity-keycloak-1.2.0.jar!/:1.2.0]
  | at org.camunda.bpm.extension.keycloak.KeycloakGroupQuery.executeList(KeycloakGroupQuery.java:36) ~[camunda-bpm-identity-keycloak-1.2.0.jar!/:1.2.0]
  | at org.camunda.bpm.engine.impl.AbstractQuery.evaluateExpressionsAndExecuteList(AbstractQuery.java:191) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.AbstractQuery.execute(AbstractQuery.java:168) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:28) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:107) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:46) ~[camunda-engine-spring-7.11.0.jar!/:7.11.0]
  | at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.1.9.RELEASE.jar!/:5.1.9.RELEASE]
  | at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:44) ~[camunda-engine-spring-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at org.camunda.bpm.engine.impl.AbstractQuery.list(AbstractQuery.java:142) ~[camunda-engine-7.11.0.jar!/:7.11.0]
  | at it.infn.sinsinfo.microservice.workflow.workflowservice.configuration.KKAuthenticationProvider.getUserGroups(KKAuthenticationProvider.java:53) ~[classes!/:na]

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.