Authorization does not apply to REST API

Hi,

TL;DR

we enabled authorization. It works in the Camunda Cockpit, but on the REST API I have full access to all resources.

Details

We use Camunda engine inside a Spring Boot application. We enabled authentication for both web apps and REST API and configured Spring security to authenticate against an external OpenId connect identity provider. We enabled authorization by setting authorization.enabled = true.

For testing, we have two groups:

  • camunda-admin: generated by Camunda, full access to everything
  • partial-access: access to one specific process definition
    Each group contains one user.

Cockpit shows all process definitions to the admin user and one process definition to the partial-access user, as expected.

But if I query the REST API directly with Postman, I always get all process definitions, no matter if I include a bearer token for the admin user or the partial-access user.

I’m sure authentication works properly, because if I send an invalid token (e.g. the required scope is missing), then the REST API returns 401-Unauthorized.

What is missing to apply authorization to the REST API?

1 Like

Hi @mba,

using the spring-boot-starter you have to enable the authorization explitly:

camunda.bpm.authorization .enabled Enables authorization Camunda default value

The default value is false.

Hope this helps, Ingo

Hi @Ingo_Richtsmeier ,
I have in application.yaml:

camunda.bpm:  
  authorization:
    enabled: true

Is there any other place I need for activating authorization in the REST API.

Hi @mba,

authorization together with authentication (code/snippets/springboot-rest-api-basic-auth at master · camunda-consulting/code · GitHub) would do the trick.

The AuthenticationProvider is important: Configure Authentication | docs.camunda.org

Hope this helps, Ingo

Hi @Ingo_Richtsmeier ,
I double checked authentication is configured properly, but didn’t find any error. In contrast to the docs, I use an OpenId connect identity provider and bearer tokens instead of basic auth, but I think this shouldn’t be the issue.

This is my RestSecurityConfig:

@Configuration
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.csrf().disable().authorizeRequests().antMatchers("/camunda/app/**", "/camunda/api/**").authenticated().and().oauth2Login();     

       http.cors()
           .and()
           .authorizeRequests()
           .antMatchers("/engine-rest/version").permitAll()  // version API needs to be open for other purpose
           .and()
           .authorizeRequests().antMatchers("/engine-rest/**").hasAuthority("SCOPE_camundaRestApi").anyRequest()
           .authenticated().and().oauth2ResourceServer().jwt();
   }

   @Bean
   public FilterRegistrationBean<ContainerBasedAuthenticationFilter> containerBasedAuthenticationFilter() {
       FilterRegistrationBean<ContainerBasedAuthenticationFilter> filterRegistration = new FilterRegistrationBean<>();
       filterRegistration.setFilter(new ContainerBasedAuthenticationFilter());
       filterRegistration
               .setInitParameters(Collections.singletonMap("authentication-provider", WebFilter.class.getCanonicalName()));
       filterRegistration.setOrder(102); // make sure the filter is registered after the Spring Security Filter Chain
       filterRegistration.addUrlPatterns("/camunda/app/*" , "/camunda/api/*", "/engine-rest/*");
       return filterRegistration;
   }

   // ...
}

The proof that authentication works properly (direct requests with Postman):

  • If I send a request without token, I receive 401.
  • If I send a request with token, but the token does not include the scope camundaRestApi, I receive 401.

Any idea what else could be missing?

After learning a lot about filters and digging in the code and the docs, I finally got it. Since it took me a lot of time, I’ll try to explain it for other Camunda/Spring Boot newbies.

We need acutally 3 servlet filters:

  1. The Spring security filter chain
  2. ContainerBasedAuthenticationFilter (from org.camunda.bpm.webapp.impl.security.auth)
  3. ProcessEngineAuthenticationFilter (from org.camunda.bpm.engine.rest.security.auth)

The Spring security filter chain is responsible for authentication on the level of the Spring Boot application. It writes the authentication result to the general Spring SecurityContext, the Camunda engine does not know about it.

The second and third filter are responsible for writing the authentication to the Camunda engine. If there is no authentication written to the engine, the engine won’t check authorization.

  • The ContainerBasedAuthenticationFilter only applies to APIs belonging to the web apps (routes starting with /app or /api),
  • The ProcessEngineAuthenticationFilter only applies to the REST API.

Except applying to different URLs, the second and third filter work pretty similar. Both expect an authenciation provider as init parameter.
This means you have to specify a class implementing org.camunda.bpm.engine.rest.security.auth.AuthenticationProvider. In this class you have to construct a org.camunda.bpm.engine.rest.security.auth.AuthenticationResult.

In short, the AuthenticationProvider is responsible for translating the Spring SecurityContext to a Camunda authentication, thus enabling authorization for the Camunda engine.

Here is a snippet how to register the filters in java code:

    // Register the servlet filter for the WebApps
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean containerBasedAuthenticationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new ContainerBasedAuthenticationFilter());
        filterRegistration
                .setInitParameters(Collections.singletonMap("authentication-provider", WebAppAuthProvider.class.getCanonicalName()));
        filterRegistration.setOrder(102); // make sure the filter is registered after the Spring Security Filter Chain
        filterRegistration.addUrlPatterns("/camunda/app/*" , "/camunda/api/*");
        return filterRegistration;
    }
    
    // Register the servlet filter for the REST API
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean camundaRestAuthFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new ProcessEngineAuthenticationFilter());
        filterRegistration
        .setInitParameters(Collections.singletonMap("authentication-provider", RestApiAuthProvider.class.getCanonicalName()));
        filterRegistration.setOrder(103); // make sure the filter is registered after the Spring Security Filter Chain
        filterRegistration.addUrlPatterns("/engine-rest/*");
        return filterRegistration;
    }
3 Likes

Hi @mba,

thank you for sharing your solution!

Lucky before your filter solution @mba , above configuration worked for me.