Single-Sign-On in Camunda

Hi all,

I’m currently trying to implement a SSO mechanism that allows me to open camunda tasklist already logged in. I’ve searched for some ways to implement what i’m trying to achieve but i can’t seem to make it work.

My problem is similar to the @Pieter_Vincken, but i want to auto-login at camunda tasklist when redirected from other site.

Can someone give me an advice to make this work? Is it even possible?

Regards,
Carlos Silva

Via this solution I’'ve built CAS SSO with Camunda: https://github.com/camunda-consulting/camunda-webapp-plugins/tree/master/camunda-webapp-plugin-sso-autologin

But i can not auto logout CAS after logout camunda.
Any one could help me

Hi @Pieter_Vincken

Would you be able to make a step by step guide for me? I’m new to camunda and would like to implement custom authentication using auth0.

Thanks
Julian

Hi,
I faced the same problem, did you find a solution?

Hi guys,

There is another SSO example here if you need some inspiration.

Best,
Ben

2 Likes

Here is one that uses Basic Authentication:

package be.aquafin.bpm.custom;

import static org.camunda.bpm.engine.authorization.Permissions.ACCESS;
import static org.camunda.bpm.engine.authorization.Resources.APPLICATION;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.DatatypeConverter;

import org.camunda.bpm.BpmPlatform;
import org.camunda.bpm.engine.AuthorizationService;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.identity.Group;
import org.camunda.bpm.engine.identity.Tenant;
import org.camunda.bpm.webapp.impl.security.SecurityActions;
import org.camunda.bpm.webapp.impl.security.SecurityActions.SecurityAction;
import org.camunda.bpm.webapp.impl.security.auth.Authentication;
import org.camunda.bpm.webapp.impl.security.auth.AuthenticationFilter;
import org.camunda.bpm.webapp.impl.security.auth.Authentications;
import org.camunda.bpm.webapp.impl.security.auth.UserAuthentication;

import java.util.logging.Logger;

/**

  • Replaces {@link AuthenticationFilter} by an extension which can auto-logon a
  • user by using Basic Authentication

*/
public class LoginAuthenticationFilter implements Filter {

private static final String[] APPS = new String[] { "cockpit", "tasklist" };
private static final String APP_MARK = "/app/";
private static final String CAMUNDA_ADMIN_GROUP = "BPM-ADMIN";

private static final Logger logger = Logger.getLogger(AuthenticationFilter.class.getName());

public void init(FilterConfig arg0) throws ServletException {
}

public void destroy() {
}

public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
		throws IOException, ServletException {
	final HttpServletRequest req = (HttpServletRequest) request;

	// get authentication from session
	Authentications authentications = Authentications.getFromSession(req.getSession());

	// This function is added to the normal AuthenticationFilter
	setAutoLoginAuthentication(request, response, authentications);

	// set current authentication to the one restored from session (maybe
	// auto login was added)
	Authentications.setCurrent(authentications);

	try {
		SecurityActions.runWithAuthentications(new SecurityAction<Void>() {
			public Void execute() {
				try {
					chain.doFilter(request, response);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
				return null;
			}
		}, authentications);
	} finally {
		Authentications.clearCurrent();
		// store updated authentication object in session for next request
		Authentications.updateSession(req.getSession(), authentications);
	}
}

/**
 * Reads the auto-login-username from the URL parameters and create an
 * {@link Authentication} for it containing its groups, tenants and
 * authorized apps.
 * 
 */
protected void setAutoLoginAuthentication(final ServletRequest request, ServletResponse response, Authentications authentications) throws ServletException {
	boolean isAdmin = false;
	String username = null;
	final HttpServletRequest req = (HttpServletRequest) request;
	final ProcessEngine engine = getEngine();

	Principal principal = req.getUserPrincipal();
	if (principal != null && principal.getName() != null && !principal.getName().isEmpty()) {
		username = principal.getName();
		logger.info("principal username = " + username);
		username = username.toUpperCase();
	} else {
		String authHeader = req.getHeader("Authorization");
		if (authHeader != null && !authHeader.isEmpty()) {
			String encodedValue = authHeader.split(" ")[1];
			byte[] decoded = DatatypeConverter.parseBase64Binary(encodedValue);
			String decodedString = "";
			try {
				decodedString = new String(decoded, "UTF-8");
			} catch (Exception ex) {
				throw new RuntimeException("Kan basic authentication string niet decoderen", ex);
			}
			username = decodedString.split(":")[0];
			logger.info("basic authentication username = " + username);
			username = username.toUpperCase();
			String password = decodedString.split(":")[1];
			//AUTHENTICATION TO CAMUNDA (AD)
			if (!isAuthenticated(engine, username, password)) {
				HttpServletResponse httpServletResponse = (HttpServletResponse) response;
				try {
				    unauthorized(httpServletResponse, "Foute username of paswoord!");
				} catch (Exception ex) {
					throw new RuntimeException("sending unauthorized response failed"); 
				}
			}
			else {
				logger.info("username " + username+" is authenticated");
			}
		}
	}
	if (username != null && !username.isEmpty()) {
		// if already in the list of logged in users - nothing to do
		Authentication authentication = authentications.getAuthenticationForProcessEngine(engine.getName());
		if (authentication != null) {
			if (authentication.getName().equals(username)) {
				logger.info(" username = " + username+ " is reeds ingelogd");
				return;
			}
		}
		else {
			logger.info("username " + username+" geen authenticatie record gevonden");
		}

		List<String> groupIds = new ArrayList<String>();
		String url = req.getRequestURL().toString();
		logger.info("url = "+url);
		String appname = getApp(url);
		if (appname != null) {
			logger.info("appname " + appname);
			if (engine != null) {
				final List<Group> groupList = engine.getIdentityService().createGroupQuery().groupMember(username)
						.list();
				// transform into array of strings:
				for (Group group : groupList) {
					groupIds.add(group.getId());
					logger.info("group.getId() " + group.getId());
					if (group.getId().equals(CAMUNDA_ADMIN_GROUP)) {
						isAdmin = true;
						logger.info(" username = " + username+ " is in ADMIN group");
					}
				}
			}
			AuthorizationService authorizationService = engine.getAuthorizationService();
			List<String> tenantIds = getTenantsOfUser(engine, username);
			// check user's app authorizations by iterating of
			// list of apps and ask if permitted
			HashSet<String> authorizedApps = new HashSet<String>();
			if (engine.getProcessEngineConfiguration().isAuthorizationEnabled()) {
				for (String application : APPS) {
					if (authorizationService.isUserAuthorized(username, groupIds, ACCESS, APPLICATION,
							application)) {
						logger.info(" username = " + username+ " heeft toegang tot " + application);
						authorizedApps.add(application);
					}
				}
				if (isAdmin)
					authorizedApps.add("admin");
			} else {
				Collections.addAll(authorizedApps, APPS);
				authorizedApps.add("admin");
			}
			if (authorizedApps.contains(appname)) {
				// create new authentication object to store
				// authentication
				UserAuthentication newAuthentication = new UserAuthentication(username, engine.getName());
				newAuthentication.setGroupIds(groupIds);
				newAuthentication.setTenantIds(tenantIds);
				newAuthentication.setAuthorizedApps(authorizedApps);
				// and add the new logged in user
				authentications.addAuthentication(newAuthentication);
			}
		}
		else {
			logger.info("geen app in url?");
		}
	} 
}

protected boolean isAuthenticated(ProcessEngine engine, String userName, String password) {
	return engine.getIdentityService().checkPassword(userName, password);
}

/**
 * copied from
 * org.camunda.bpm.webapp.impl.security.auth.UserAuthenticationResource
 */
protected List<String> getGroupsOfUser(ProcessEngine engine, String userId) {
	List<Group> groups = engine.getIdentityService().createGroupQuery().groupMember(userId).list();
	List<String> groupIds = new ArrayList<String>();
	for (Group group : groups) {
		groupIds.add(group.getId());
	}
	return groupIds;
}

private String getApp(String url) {
	String app = null;
	int index = url.indexOf(APP_MARK);
	if (index >= 0) {
		String urlLastPart = url.substring(index + APP_MARK.length());
		index= urlLastPart.indexOf("/");
		if (index >= 0) {
			app = urlLastPart.substring(0,index);
		}
		else {
			app = urlLastPart;
		}
	}
	return app;
}

protected List<String> getTenantsOfUser(ProcessEngine engine, String userId) {
	List<Tenant> tenants = engine.getIdentityService().createTenantQuery().userMember(userId)
			.includingGroupsOfUser(true).list();

	List<String> tenantIds = new ArrayList<String>();
	for (Tenant tenant : tenants) {
		tenantIds.add(tenant.getId());
	}
	return tenantIds;
}

private ProcessEngine getEngine() {
	return BpmPlatform.getDefaultProcessEngine();
}

private void unauthorized(HttpServletResponse response, String message) throws IOException {
    response.setHeader("WWW-Authenticate", "Basic realm=Protected");
    response.sendError(401, message);
  }

  private void unauthorized(HttpServletResponse response) throws IOException {
    unauthorized(response, "Unauthorized");
  }

Hi @speetrs , we had the same piece of code for the already sso’ed session. But we get
below error :
unable to create session after the response has been commited

With this approach one can only access Rest api’s of engine by passing jwt token. But How one can login to Camunda Web apps using jwt ?