Camunda Auto Login

Venkat Pandeti
3 min readSep 28, 2020

Camunda is a popular open-source workflow and decision automation platform. Camunda Business Process Management (Camunda BPM) is a light weight Java-based framework. It can be embedded inside Java server application using Camunda Maven dependency and Spring Boot.

Camunda BPM provides built-in user authentication. It doesn’t provide Auto Login capability with no custom authentication and authorization model. Few tweaks are required to achieve it. Lets go through them.

Dependencies:
Spring Boot - 2.3.4.RELEASE
org.camunda.bpm.springboot: 3.4.0
com.h2database: 1.4.200

Camunda UI url patterns are “/app/*”, “/api/*” and “/lib/*”. When any http request with the above url patterns is received, it should be intercepted and passed to Camunda Authentication Filter to provide custom authentication and authorization. Great article on Spring security https://spring.io/guides/topicals/spring-security-architecture. WebSecurityConfig registers a filter bean to intercept all the Camunda UI requests and pass them to the CamundaAuthFilter

@Configuration
public class WebSecurityConfig {

@Bean
public FilterRegistrationBean authFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new CamundaAuthFilter());
filterRegistration.setInitParameters(Collections.singletonMap("authentication-provider", CamundaAuthProvider.class.getCanonicalName()));
filterRegistration.addUrlPatterns("/app/*", "/api/*", "/lib/*"); // Camunda UI url patterns
return filterRegistration;
}
}

CamundaAuthFilter is a modified version of Camunda provided ContainerBasedAuthenticationFilter class. It uses CamundaAuthProvider to check if the user is authenticated or not.

Request is passed to the doFilter method of CamundaAuthFilter class

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse)response;
String engineName = this.extractEngineName(req);
if (engineName == null) {
chain.doFilter(request, response);
} else {
ProcessEngine engine = this.getAddressedEngine(engineName);
if (engine == null) {
resp.sendError(404, "Process engine " + engineName + " not available");
} else {
AuthenticationResult authenticationResult = this.authenticationProvider.extractAuthenticatedUser(req, engine);
if (authenticationResult.isAuthenticated()) {
Authentications authentications = Authentications.getFromSession(req.getSession());
String authenticatedUser = authenticationResult.getAuthenticatedUser();
if (!this.existisAuthentication(authentications, engineName, authenticatedUser)) {
List<String> groups = authenticationResult.getGroups();
List<String> tenants = authenticationResult.getTenants();
camundaAuthzService.addAuthorization(authenticatedUser, engine.getAuthorizationService());
Authentication authentication = this.createAuthentication(engine, authenticatedUser, groups, tenants);
authentications.addAuthentication(authentication);
}

chain.doFilter(request, response);
} else {
resp.setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
this.authenticationProvider.augmentResponseByAuthenticationChallenge(resp, engine);
}

}
}
}

CamundaAuthProvider: In general, this is where we should add the authentication logic. To make it simple, I am always returning the authenticated AuthenticationResult with the default user id “Camunda User

public class CamundaAuthProvider implements AuthenticationProvider {

private static final String USER_ID = "Camunda User";

@Override
public AuthenticationResult extractAuthenticatedUser(HttpServletRequest httpServletRequest, ProcessEngine processEngine) {
Authentications authentications = Authentications.getFromSession(httpServletRequest.getSession());
String userId = USER_ID;
if(authentications != null && authentications.hasAuthenticationForProcessEngine(processEngine.getName())) {
userId = authentications.getAuthenticationForProcessEngine(processEngine.getName()).getName();
}
AuthenticationResult authenticationResult = new AuthenticationResult(userId, true);
authenticationResult.setGroups(new ArrayList<>());
return authenticationResult;
}

@Override
public void augmentResponseByAuthenticationChallenge(HttpServletResponse httpServletResponse, ProcessEngine processEngine) {

}
}

We are done with the authentication. Lets move on to the authorization. Camunda supports RBAC authorization model. To make it simple, I have added the authorization for the user “Camunda User” for all the resources.

CamundaAuthzService uses the Camunda Authorization Service to check if the user has authorization for the resources. If authorization doesn’t exist, it will be added.

public class CamundaAuthzService {
void addAuthorization(String userId, AuthorizationService authorizationService) {
Arrays.stream(Resources.values()).forEach(resource -> {
boolean authzExists = authzExists(userId, authorizationService, Authorization.AUTH_TYPE_GRANT, Permissions.ALL, resource, "*");
if(!authzExists) {
Authorization authz = authorizationService.createNewAuthorization(Authorization.AUTH_TYPE_GRANT);
authz.setUserId(userId);
authz.addPermission(Permissions.ALL);
authz.setResource(resource);
authz.setResourceId("*");
authorizationService.saveAuthorization(authz);
}
});
}

private boolean authzExists(String userId, AuthorizationService authorizationService, int authzType, Permissions permissions, Resource resource, String resourceId) {
Authorization authz = authorizationService.createAuthorizationQuery()
.userIdIn(userId)
.authorizationType(authzType)
.hasPermission(permissions)
.resourceType(resource)
.resourceId(resourceId)
.singleResult();

return authz != null;
}
}

application.properties: Camunda authorization should be enabled to provide the fine-grained authorization

spring.h2.console.enabled=true
spring.h2.console.path
=/h2

camunda.bpm.admin-user.id
=admin
camunda.bpm.admin-user.password
=admin
camunda.bpm.authorization.enabled
=true

With all the above code, I was able to accomplish Camunda Auto Login. When I ran my spring boot application and entered http://localhost:8080/ in the browser, below Camunda home page appeared without the login page, with the username “Camunda User”

For the source code, visit my GitHub repository: https://github.com/vpandeti/camunda-sso.

Happy coding :)

--

--