Java – Spring Boot OAuth2 with basic authentication and custom UserDetailsService

javaoauth-2.0spring-bootspring-oauth2spring-security

I'm trying to configure an OAuth2 server that will be capable to complete the basic OAuth2 flows (see here for examples).

Apologies for the long question

My first attempt is to be able to perform an authorization_code flow.

I have the following configuration:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig
        extends WebSecurityConfigurerAdapter {

    ....

        @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
              .userDetailsService(userDetailsService)
              .passwordEncoder(passwordEncoder());
    }

...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin().loginPage("/login").permitAll()
                .and()
                .requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }

...

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

My Server configuration

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorServerConfig
        extends AuthorizationServerConfigurerAdapter {

    @Inject
    private TokenStore tokenStore;

    @Inject
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

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

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        .inMemory()
                .withClient("foo")
                .secret("foo")
                .authorizedGrantTypes("authorization_code","password", "refresh_token")
                .scopes(new String[] { "read", "write" })
    }

And indeed my authorization code flow works ok! Problem start when I try to do a password flow as follows:

POST localhost:8200/oauth/token
Content-Type: application/x-www-form-urlencoded
Accept:application/json
Authorization:Basic Zm9vOmZvbw=="
username=admin&password=admin&grant_type=password&scope=read%20write&client_secret=foo&client_id=foo&

My problem is that the Authorization header is ignored, whether it is there or not the result is the same, it gives me back the access_token and refresh_token

If I try to enable the basic authentication as follows, enable ClientUserDetailsService, reading clients from database JDBC:

public class SecurityConfig
        extends WebSecurityConfigurerAdapter {

    ....

@Bean
public ClientDetailsUserDetailsService   clientDetailsUserDetailsService(ClientDetailsService clientDetailsService){
    // JDBC clientDetailsService
    return new ClientDetailsUserDetailsService(clientDetailsService);
}


        @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
              .userDetailsService(clientDetailsUserDetailsService());
    }

...

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    DaoAuthenticationProvider p = new DaoAuthenticationProvider();
    p.setUserDetailsService(userDetailsService);
    p.setPasswordEncoder(passwordEncoder());
    return new ProviderManager(Lists.newArrayList(p));
    //        return super.authenticationManagerBean();
    }

    }

Now what I have achieved is to have the basic authentication working properly BUT i loose the authorization_code flow since the basic authentication now it is done against the clientid and secret and NOT the user's actual credentials

Am I missing something? How can I have both flows? Please help, I'm struggling for some days now.

Some Similar questions, but without any luck:

Best Answer

I think this is because you've turned on form authentication for clients, you it's using that instead of the Basic header.

public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security
        .allowFormAuthenticationForClients()
        .checkTokenAccess("authenticated()");
}

Take out .allowFormAuthenticationForClients().

Related Topic