Search code examples
spring-bootspring-cloudspring-oauth2

Upgrading to spring-boot 1.4.1 - OAuth2 Security integration tests pass but client apps cannot log in


I have just upgraded an app from spring-boot 1.3.5 to spring-boot 1.4.1

For some reason, all my integration tests pass using oauth2. The test client can register users and perform operations with them during the maven integration test phase.

However when I start up my API server (which also contains the resource and authorization servers currently) clients can no longer get a bearer token.

My ClientDetailsService is never called on client login - but is registered with

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

on application startup.

Any ideas anyone?

Here is the relevant code:

Authorization Server Config:

import javax.inject.Inject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import blablabla.api.security.ClientDetailsServiceImpl;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Inject
    private AuthenticationManager authenticationManager;

    @Inject
    private ClientDetailsServiceImpl clientDetailsService;

    @Inject
    private UserDetailsService userDetailsService;

    public AuthorizationServerConfiguration() {
        System.out.println("loaded");
    }

    // beans

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() { 
        final JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        // FIXME: Include a more secure key in the java token store
        accessTokenConverter.setSigningKey("3434343434");
        return accessTokenConverter;
    }

    @Bean
    public TokenStore tokenStore() {
        //return new InMemoryTokenStore();

        return new JwtTokenStore(accessTokenConverter());
    }

    // config

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
        endpoints.userDetailsService(userDetailsService);
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }
}

Resource Server Config:

import javax.inject.Inject;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import blablabla.security_lib.config.UserDetailsServiceImpl;

@Configuration
@EnableResourceServer
@EnableWebSecurity
@ComponentScan("blablabla.api.security")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Inject
    private UserDetailsServiceImpl userDetailsService;

    @Inject
    private TokenStore tokenStore;

    // Beans

    @Bean
    public TokenStore tokenStore() {
        //return new InMemoryTokenStore();
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() { 
        final JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("3434343434");
        return accessTokenConverter;
    }

    // global security concerns

    @Bean
    public AuthenticationProvider authProvider() { 
        final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        return authProvider;
    }

    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth) { 
        auth.authenticationProvider(authProvider());
    }

    @Primary
    @Bean
    public ResourceServerTokenServices getTokenServices () { 
        DefaultTokenServices services = new DefaultTokenServices();
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore);
        return services;
    }

    // http security concerns

    @Override
    public void configure(HttpSecurity http) throws Exception { 
        http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();

        /*http
        .authorizeRequests().regexMatchers("^/members").anonymous()
        .and()
        .authorizeRequests().anyRequest().authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .csrf()
        .disable();*/
    }

}

A few things I've also noticed - this all might mean something to somebody.

When login does not work (starting up the api and using a client)

The org.springframework.security.web.access.expression.WebExpressionVoter is passed a Collection containing one attribute of type org.springframework.security.web.access.expression.WebExpressionConfigAttribute with the value of "#oauth2.throwOnError(authenticated)"

When login does work (running an acceptance test)

The org.springframework.security.web.access.expression.WebExpressionVoter is passed a Collection containing one attribute of type org.springframework.security.web.access.expression.WebExpressionConfigAttribute with the value of "[fullyAuthenticated]"


Solution

  • So this turned out to not be a spring-boot issue at all, but instead some kind of issue that was caused in our gateway server when upgrading from spring-cloud Brixton.M2 release train to Brixton.RELEASE. This is sort of an unhelpful answer to an unhelpful question, so - happy to see this removed from SO.