Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No AuthenticationProvider found on refresh token - Spring OAuth2 java config

I have a Spring Boot project in which I've configured a Spring OAuth2 authentication process which partially works. I can authenticate OK but when I'm trying to get a refresh token I get an exception.

OAuth configuration:

@Configuration
public class OAuth2ServerConfiguration {

    private static final String RESOURCE_ID = "xxx";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests()
            .antMatchers("/api/**").authenticated();
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Value("${clientDetailsService.clientName}")
        private String clientName;

        @Value("${clientDetailsService.clientSecret}")
        private String clientSecret;

        @Autowired
        @Qualifier("authenticationManager")
        private AuthenticationManager authenticationManager;

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        @Qualifier("tokenServices")
        private AuthorizationServerTokenServices tokenServices;

        @Autowired
        @Qualifier("codeServices")
        private AuthorizationCodeServices codeServices;

        @Autowired
        @Qualifier("requestFactory")
        private OAuth2RequestFactory requestFactory;

        @Autowired
        @Qualifier("tokenGranter")
        private TokenGranter tokenGranter;

        private final TokenStore tokenStore = new InMemoryTokenStore();

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.setClientDetailsService(clientDetailsService);
            endpoints.tokenServices(tokenServices)
                        .tokenStore(tokenStore)
                        .authorizationCodeServices(codeServices)
                        .authenticationManager(authenticationManager)
                        .requestFactory(requestFactory)
                        .tokenGranter(tokenGranter);
        }

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


        @Bean(name = "tokenGranter")
        @Primary
        public TokenGranter tokenGranter() {
            final List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();

            tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, codeServices, clientDetailsService, requestFactory));
            tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));

            return new CompositeTokenGranter(tokenGranters);
        }

        @Bean
        @Primary
        public ClientDetailsService clientDetailsService(){
            final InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder();
            builder.withClient(clientName)
                    .authorizedGrantTypes("password", "refresh_token")
                    .authorities("USER")
                    .scopes("read", "write")
                    .resourceIds(RESOURCE_ID)
                    .secret(clientSecret);

            try {
                return builder.build();
            } catch (final Exception e) {
                e.printStackTrace();
            }

            return null;

        }

        @Bean(name = "tokenServices")
        @Primary
        public AuthorizationServerTokenServices tokenServices() {
            final DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setClientDetailsService(clientDetailsService);
            tokenServices.setTokenStore(tokenStore);
            tokenServices.setAuthenticationManager(authenticationManager);
            return tokenServices;
        }

        @Bean(name = "requestFactory")
        @Primary
        public OAuth2RequestFactory requestFactory() {
            return new DefaultOAuth2RequestFactory(clientDetailsService);
        }

        @Bean(name = "codeServices")
        @Primary
        public AuthorizationCodeServices authorizationCodeServices() {
            return new InMemoryAuthorizationCodeServices();
        }
    }

I also have some custom components defined, like a custom Token Granter, custom authentication provider etc. I'll post them if necessary.

As I said, authentication flow works OK. When I POST to /oauth/token I get a token and a refresh token, but when I then try to exchange my refresh token for a new token (by POSTing http://localhost:8080/oauth/token with grant_type=refresh_token and refresh_token=my refresh token) I get an exception:

No AuthenticationProvider found for org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken

Where do I set the authentication provider? How do I get Spring to use my custom authentication provider for refresh tokens also?

like image 865
Jesper N Avatar asked Jan 11 '16 08:01

Jesper N


2 Answers

I fixed this by explicitly defining a PreAuthenticationProvider:

@Component("preAuthProvider")
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider {

    @Autowired
    private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userService;

    public CustomPreAuthProvider(){
        super();
    }

    @PostConstruct
    public void init(){
        super.setPreAuthenticatedUserDetailsService(userService);
    }
}

and then a custom userservice:

@Service
public class CustomPreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {

    @Override
    public final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) {
    ...    
    }
}

and adding this provider to the oauth2 config:

@Autowired
private AuthenticationProvider authenticationProvider;

@Autowired
@Qualifier("preAuthProvider")
private AuthenticationProvider preAuthProvider;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider).authenticationProvider(preAuthProvider);
}
like image 144
Jesper N Avatar answered Nov 09 '22 05:11

Jesper N


As an alternative way to Jesper's answer, if you want to reuse your current UserDetailsService for this purpose, you can just do it the same way as Spring does it with their DefaultTokenServices:

@Bean
public CustomTokenServices tokenServices() {
    CustomTokenServices tokenServices = new CustomTokenServices();
    tokenServices.setTokenStore(tokenStore());
    tokenServices.setSupportRefreshToken(true);
    tokenServices.setReuseRefreshToken(false);
    tokenServices.setClientDetailsService(clientDetailsService);
    tokenServices.setAuthenticationManager(createPreAuthProvider());
    return tokenServices;
}

private ProviderManager createPreAuthProvider() {
    PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
    provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
    return new ProviderManager(Arrays.asList(provider));
}

UserDetailsService is autowired into this @Configuration class.

like image 35
Sikor Avatar answered Nov 09 '22 06:11

Sikor