6

I'm trying to access a web service which is protected by Spring Security using ResourceServerConfigurerAdapter (with Oauth2 client_credentials)

Following is the security configuration

//Micoservice 1
@Configuration
@EnableResourceServer
class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Autowired
    private Environment env;

    @Autowired
    private DummyUserFilter dummyUserFilter;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(dummyUserFilter, LogoutFilter.class)
            .formLogin().disable()
            .httpBasic().disable()
            .csrf().disable()
            .antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/js/**", "/webjars/**").permitAll()
            .anyRequest()
                .authenticated();
    }
}

This application (Microservice 1) is to be accessed by another application (Microservice 2) with the Oauth2RestTemplate. Following is the Oauth2RestTemplate configuration.

//MicroService 2
@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
        resourceDetails.setAccessTokenUri("https://<UAA>/oauth/token");
        resourceDetails.setClientId("#####");
        resourceDetails.setClientSecret("#####");
        resourceDetails.setGrantType("client_credentials");
        resourceDetails.setTokenName("access_token");

        DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext();

        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext);

        return restTemplate;
    }
}

Microservice 2 has various web services which use RestTemplate to access the protected web services of Microservice 1.

This always results in following exception

Authentication is required to obtain an access token (anonymous not allowed)

I have searched for this error and found that it's thrown in AccessTokenProviderChain

Here's the link from github for the relevant code

https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/token/AccessTokenProviderChain.java#L89

if (auth instanceof AnonymousAuthenticationToken) {
    if (!resource.isClientOnly()) {
        throw new InsufficientAuthenticationException(
                "Authentication is required to obtain an access token (anonymous not allowed)");
    }
}

It seems that it doesn't allow anonymous user to get access to the Oauth2 token.

I have no intention of protecting the client application (Microservice 2) with Oauth2 and I must use client_credentials for the Oauth2RestTemplate, that's preconfigured.

How can I stop Spring from blocking anonymous user from accessing token ?

I have already tried to populate SecurityContextHolder with dummy authentication in case of anonymouse user, with no success. Even if I do succeed in doing so, it seems like a hack.

11thdimension
  • 10,333
  • 4
  • 33
  • 71
  • For now I have solved it by separating creation of `Oauth2RestTemplate` completely from Spring. If I create a bean for `Oauth2RestTemplate` or try to autowire it, Spring detects and tries to interfere with it. So I created my own singleton class for `Oauth2RestTemplate` which has nothing to with Spring, for now it works. However I'm looking forward to a real answer. – 11thdimension May 11 '17 at 13:11
  • Have you tried defining the `restTemplate` bean of type `OAuth2RestTemplate` instead of `RestTemplate `, and `@autowire`ing , with `OAuth2RestTemplate restTemplate `? On the other hand, it doesn't make sense to throw `InsufficientAuthenticationException` if you are using `ClientCredentialResourceDetails` as it clearly returns `true` for `isClientOnly()` https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/token/grant/client/ClientCredentialsResourceDetails.java. Any chance other Bean in place? – user2829759 May 03 '18 at 18:45
  • Yes, I have used Oauth2RestTemplate, as you can see in the code I posted in the question, it throws the same error. I don't think there's another bean, for now it's working by creating custom singleton. – 11thdimension May 15 '18 at 23:02

3 Answers3

7

I had the same problem and concluded that the AccessTokenProvider interface, which has multiple implementors is the culprit. By default, the OAuth2RestTemplate instantiates the AccessTokenProviderChain which tries to re-use the existing login context. However, if no such login exists, this is bound to fail. Try

restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());

in your factory method. This uses a token provider which owns its credentials and doesn't reference the thread local SecurityContextHolder in its implementation at all.

Yasammez
  • 1,383
  • 12
  • 15
1

Try Enabling httpBasic() security in your 2nd microservice and login to it using default username ("user") and generated password by spring.

  • That would require the user to be known by Spring, however authentication mechanism required is `client_credential` which doesn't involve a user. – 11thdimension Apr 23 '18 at 23:31
  • Sorry, Previously I mentioned 1st microservice. I corrected it as 2nd microservice. I edited my answer. Try login to 2nd microservice as I mentioned in the answer. – Chatura Galagama Apr 25 '18 at 06:20
  • I will give it a try, but why would that be required? – 11thdimension May 15 '18 at 23:07
  • As per my knowledge, That's because spring doesn't allow anonymous users to proceed with Oauth2. "Authentication is required to obtain an access token (anonymous not allowed)" – Chatura Galagama May 16 '18 at 10:29
0

use AuthorizedFeignClient or AuthorizedUserFeignClient instead oauth2RestTemplate...

Saahon
  • 404
  • 1
  • 6
  • 27