9

I am using Spring Security OAuth2 for authorizations. When trying to refresh the token I get an error: UserDetailsService is required (interestingly I get this error only on unix machines and not on windows). I am using Spring OAuth2 version 2.0.7.

For some reason the AuthenticationManager in the DefaultTokenService is not empty and it tries to authenticate the user to check if he still exists. I think it gets initialized because of some spring security vs. spring oauth2 configuration problems.

I am not using any custom UserDetailsService, hence it should not authenticate the users at this point. However, when I debug it I see that it tries to use one from the WebSecurityConfigurerAdapter and gets to this error. Even if I provide my custom dummy UserDetailsService, it is not using that one, but tries to use the other one, which is null. Am I missing here something? I can not find out why is this happening?

Here is my Oauth2 configuration

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private MySpringTokenStore tokenStore;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyClientDetailsServiceImpl clientDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore);
        endpoints.authenticationManager(authenticationManager)
          .approvalStoreDisabled();
    }

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
    }

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

Here is my Spring security configuration

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
        .authorizeRequests()
            .antMatchers("/myRest/events/**", "/events/**", "/events", "/myRest/events").permitAll() 
            .antMatchers("/login.jsp", "/login").permitAll() 
        .and()
            .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
            .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/myRest/events")).disable()
        .sessionManagement().sessionFixation().none();
        // @formatter:on
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/index*", "/myRest/events/**", "/events/**", "/myRest/events", "/events", "/swagger/**", "/kibana/**",
            "/elastic/**", "/version/**", "/api-docs/**", "/js/**", "/oauth/uncache_approvals", "/oauth/cache_approvals");
    }
}
Ortomala Lokni
  • 56,620
  • 24
  • 188
  • 240
Filip Majernik
  • 7,700
  • 13
  • 45
  • 53

4 Answers4

17

Authorization server endpoint needs UserDetailsService. In your OAuth2Config class configure user details service like the following:

@Autowired
private UserDetailsService userDetailsService;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore);
    endpoints.userDetailsService(userDetailsService);
    endpoints.authenticationManager(authenticationManager)
      .approvalStoreDisabled();
}

You can also configure it in WebSecurityConfigurerAdapter:

@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;

@Override
protected void configure(HttpSecurity http) throws Exception {

    if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
        UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
        endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
    }

    // @formatter:off
    http
    .authorizeRequests()
        .antMatchers("/myRest/events/**", "/events/**", "/events", "/myRest/events").permitAll() 
        .antMatchers("/login.jsp", "/login").permitAll() 
    .and()
        .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
        .csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/myRest/events")).disable()
    .sessionManagement().sessionFixation().none();
    // @formatter:on
}
Vijay Nandwana
  • 2,476
  • 4
  • 25
  • 42
  • 2
    No, that is not true. The Oauth2 configuration doesn't need that service, it can also be left empty. I've tried also to provide my implementation of that service and it also threw the same exception. I have already solved this issue. The problem was the order of the configurations (because they sahre some objects). I have set the order of the OAuth2 configuration to be (for example) 100 and the Web Security configuration to be 101. This way it works. – Filip Majernik May 11 '17 at 08:48
  • I was getting same issue..Adding endpoints.userDetailsService(userDetailsService) on configure(AuthorizationServerEndpointsConfigurer endpoints) worked for me., provided I was returning a custom UserDetailsService with mock userDetails.. – roscodex Feb 23 '23 at 18:48
1

If implementing custom DefaultTokenServices, we don't need UserDetailsService.

@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
            // ...
            .tokenServices(tokenServices(endpoints));
    }

    public AuthorizationServerTokenServices tokenServices(final AuthorizationServerEndpointsConfigurer endpoints) {
        final DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        // ...
        tokenServices.setAuthenticationManager(
            new ProviderManager(List.of(new MyCustomAuthProvider())));
        return tokenServices;
    }
}

The commit message says:

Add AuthenticationManager to default token services

So that it can be used to check user account changes in a refresh token grant. If a global UserDetailsService is available it will be used as a default (e.g. if user has a GlobalAuthenticationConfigurer). It works by constructing a PreAuthenticationAuthenticationProvider and using that the authenticate the user in DefaultTokenServices. To customize that process, users can create their own DefaultTokenServices and inject an AuthenticationManager.

Fixes gh-401

1

Adding on to @VijayaNandwana's answer and considering @FilipMajernik's comment, I created a class for OAuthConfig and made the order less than the class which extends WebSecurityConfigurerAdapter.

@Configuration
@Order(1)
    public class OAuthConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;

@Autowired
private JdbcTemplate jdbcTemplate;

@Bean
public TokenStore tokenStore() {
    return new JdbcTokenStore(jdbcTemplate.getDataSource());
}

@Autowired
private AuthenticationManager authenticationManager;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore());
    endpoints.userDetailsService(userDetailsService);
    endpoints.authenticationManager(authenticationManager)
                .approvalStoreDisabled();
    }
}

And Class which extends WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
@Order(2)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  //Configurations
}
nandeesh
  • 753
  • 7
  • 16
0

The authorisation endpoint requires a UserDetailsService.

Add this:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore());
    endpoints.userDetailsService(userDetailsService);
    endpoints.authenticationManager(authenticationManager)
                .approvalStoreDisabled();
    }
}