title: Combining Oauth2 with formlogin and actuator security in Spring Boot
body: <p>I am using Spring Boot 1.5.9 and have an application that has an API that uses OAuth2 client credentials, with formlogin for a CMS that uses Thymeleaf in the same Spring Boot application.</p>
  
  <p>For this to work, I have the following bean to configure the form login:</p>
  
  <pre><code>@Configuration
  public class WebSecurityGlobalConfig extends WebSecurityConfigurerAdapter {
  
      @Autowired
      private UserDetailsService userDetailsService;
      @Autowired
      private PasswordEncoder passwordEncoder;
  
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  
          auth.userDetailsService(userDetailsService)
              .passwordEncoder(passwordEncoder);
      }
  
      @Override
      public void configure(WebSecurity web) throws Exception {
          web.ignoring()
             .antMatchers(HttpMethod.OPTIONS);
      }
  
      @Override
      public void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests()
              // api security is handled elsewhere (See OAuth2ServerConfiguration)
              .antMatchers("/api/**", "/oauth/**", "/management/**")
              .permitAll()
              // end api security
              .anyRequest().hasRole(UserRole.ADMIN.name())
              .and()
              .formLogin().loginPage("/login")
              .permitAll()
              .and()
              .logout().permitAll();
      }
  }
  </code></pre>
  
  <p>So for the form login part, I declare everything related to API, Oauth and /management (the custom context-path I have set in <code>application.properties</code> for the actuator endpoints):</p>
  
  <pre><code>management.context-path=/management
  management.security.roles=ADMIN
  </code></pre>
  
  <p>For Oauth2, I have this:</p>
  
  <pre><code>@Configuration
  public class OAuth2ServerConfiguration {
  
      private static final String RESOURCE_ID = "my-app-service";
  
      @Configuration
      @EnableResourceServer
      @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
      protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
  
          @Override
          public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
              resources.resourceId(RESOURCE_ID);
          }
  
          @Override
          public void configure(HttpSecurity http) throws Exception {
  
              http.authorizeRequests()
                  .antMatchers(HttpMethod.OPTIONS, "/api/**")
                  .permitAll()
                  .and()
                  .antMatcher("/api/**")
                  .authorizeRequests()
                  .anyRequest()
                  .authenticated()
                  .and()
                  .authorizeRequests()
                  .antMatchers("/management/health", "/management/info").permitAll()
                  .antMatchers("/management/**").hasRole(UserRole.ADMIN.name())
                  .anyRequest().authenticated();
          }
      }
  
      @Configuration
      @EnableAuthorizationServer
      protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
  
          @Autowired
          private AuthenticationManager authenticationManager;
  
          @Autowired
          private UserDetailsService userDetailsService;
  
          @Autowired
          private PasswordEncoder passwordEncoder;
  
          @Autowired
          private TokenStore tokenStore;
  
          @Autowired
          private SecurityConfiguration securityConfiguration;
  
          // NOTE: If you set a new validity, you need to clear the 'oauth_access_token' table
          // in the database. Only new tokens get the new validity.
          @Value("${myapp.security.oauth.access-token-validity-seconds:43200}") // 12 hours by default
          private int accessTokenValiditySeconds;
  
          @Value("${myapp.security.oauth.refresh-token-validity-seconds:2592000}") // 30 days by default
          private int refreshTokenValiditySeconds;
  
          @Override
          public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
              security.passwordEncoder(passwordEncoder);
          }
  
          @Override
          public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
              clients.inMemory()
                     .withClient(securityConfiguration.getMobileAppClientId())
                     .authorizedGrantTypes("password", "refresh_token")
                     .scopes("mobile_app")
                     .resourceIds(RESOURCE_ID)
                     .accessTokenValiditySeconds(accessTokenValiditySeconds)
                     .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
                     .secret(passwordEncoder.encode(securityConfiguration.getMobileAppClientSecret()));
          }
  
          @Override
          public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              endpoints.tokenStore(tokenStore).
                      authenticationManager(authenticationManager)
                       .userDetailsService(userDetailsService);
          }
      }
  }
  </code></pre>
  
  <p>I want the following behaviour:</p>
  
  <ul>
  <li>If user has role <code>ADMIN</code> by using an Oauth2 access token, all actuator endpoints must be accessible</li>
  <li>If the user does not have this <code>ADMIN</code> role, only <code>/health</code> and <code>/info</code> should be accessible (If <code>ADMIN</code>, <code>/health</code> should show extra info like it is by default)</li>
  </ul>
  
  <p>The current behaviour:</p>
  
  <p>The info and health endpoints can be viewed by everybody, but as ADMIN, you don't get extra info. For the other endpoints, I get a 401 if I try with an access token of an ADMIN user with:</p>
  
  <pre><code>{
      "timestamp": "2018-01-30T13:45:26.625+0000",
      "status": 401,
      "error": "Unauthorized",
      "message": "Full authentication is required to access this resource.",
      "path": "/management/beans"
  } 
  </code></pre>
  
  <p>If I set <code>management.security.enabled=false</code> then the ADMIN user has access, but all non-ADMIN users also have access.</p>
  
  <p>What should I change to get the wanted behaviour?</p>