dawid gdanski
dawid gdanski

Reputation: 2452

OAuth2 Spring Security - OAuth2AuthenticationProcessingFilter not working

Hey there I am implementing Spring app with OAuth2.

According to spring documentation, in order to enable resources requests with OAuth2 authorization I need to add @EnableResourceServer annotation to have OAuth2AuthenticationProcessingFilter included.

I added this annotation but unfortunately Filter is not invoked in chain on start-up.

I am able to obtain access token with curl command:

curl -X POST -H "Authorization: Basic **************************" -v -H "Accept: application/json" -d "username=my.user&password=pass&client_id=my.user&client_secret=4e1d635a-7c9d-426b-a942-cc166438f996&grant_type=password&scope=read write" http://localhost:8080/oauth/token

But resources requesting:

curl -v -H "Authorization : Bearer ecfa5bfb-c224-4b4a-abf4-cb4a828c2efb" -H "Accept: application/json" http://localhost:8443/oauth/api/meetings/9

gives:

Empty reply from server

Connection #0 to host localhost left intact

Below my resources configuration:

@Configuration
@EnableWebSecurity
@ComponentScan("com.springapp.mvc")
@EnableResourceServer
@Order(4)
public class Oauth2ResourcesConfigurationAdapter extends ResourceServerConfigurerAdapter {

@Autowired
private OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint;

@Autowired
private PreAuthUserDetailsService preAuthUserDetailsService;

@Autowired
private OAuth2AccessDeniedHandler accessDeniedHandler;

@Autowired
private DefaultTokenServices tokenServices;

@Autowired
private TokenStore tokenStore;

@Override
public void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("/oauth/api/**")
            .access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
            .accessDecisionManager(accessDecisionManager())
            .anyRequest()
            .fullyAuthenticated();
    http
            .anonymous()
            .disable();
    http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER);
    http
            .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler);
    http
            .logout()
            .logoutUrl("/oauth/logout")
            .logoutSuccessHandler(logoutSuccessHandler())
            .invalidateHttpSession(true);
    http
            .requiresChannel()
            .antMatchers("/oauth/api/**")
            .requiresSecure();
    http
            .portMapper()
            .http(8080)
            .mapsTo(8443);
}

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources
            .authenticationManager(getAuthenticationManager())
            .tokenServices(tokenServices)
            .tokenStore(tokenStore);
}

private AuthenticationManager getAuthenticationManager() {
    final OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
    oAuth2AuthenticationManager.setTokenServices(tokenServices);

    return oAuth2AuthenticationManager;
}

private PreAuthenticatedAuthenticationProvider preAuthAuthenticationProvider() {
    final PreAuthenticatedAuthenticationProvider preAuthAuthenticationProvider = new PreAuthenticatedAuthenticationProvider();
    preAuthAuthenticationProvider.setPreAuthenticatedUserDetailsService(preAuthUserDetailsService);

    return preAuthAuthenticationProvider;
}

private AccessDecisionManager accessDecisionManager() {
    return new UnanimousBased(Arrays.<AccessDecisionVoter>asList(new ScopeVoter(),
                                                                 new AuthenticatedVoter(),
                                                                 new WebExpressionVoter()));
}

private LogoutSuccessHandler logoutSuccessHandler() {
    return new OAuth2SuccessLogoutHandler(tokenStore);
}

static final class OAuth2SuccessLogoutHandler implements LogoutSuccessHandler {

    private final TokenStore tokenStore;

    public OAuth2SuccessLogoutHandler(final TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
           request.toString();
    }
}

}

My question is: where is the mistake?

Upvotes: 2

Views: 10351

Answers (2)

ejazazeem
ejazazeem

Reputation: 551

You need to review your authenticationManager Bean. And then detect request containing Authorization header or access_token request parameter in ResourceServer security configuration.

Following is a working configuration, hope this will help

@Configuration
@EnableOAuth2Sso
@EnableWebSecurity
protected static class ResourceConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${sso.url}")
    private String ssoUrl;

    @Autowired
    private  RedisConnectionFactory redisConnectionFactory;

    @Bean
    protected TokenStore tokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Bean
    @Primary
    protected ResourceServerTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);

        return defaultTokenServices;
    }


    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
        authenticationManager.setTokenServices(tokenServices());
        return authenticationManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {      
        http.requestMatchers()
        .and().authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers(HttpMethod.GET, "/static/**").permitAll()
            .antMatchers(HttpMethod.GET, "/profile/**").permitAll()
            .antMatchers(HttpMethod.GET, "/services/**").permitAll()
            .anyRequest().authenticated()
        .and().logout()
                .invalidateHttpSession(true)
                .logoutSuccessUrl(ssoUrl+"/logout")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .deleteCookies("JSESSIONID").invalidateHttpSession(true)
                .permitAll();
    }

}

@Configuration
@EnableResourceServer
@Order(1)
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter {



    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("resource-id");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.requestMatcher(new OAuthRequestedMatcher())
            .authorizeRequests().anyRequest().fullyAuthenticated();

    }
}

private static class OAuthRequestedMatcher implements RequestMatcher {
    public boolean matches(HttpServletRequest request) {
        String auth = request.getHeader("Authorization");
        boolean haveOauth2Token = (auth != null) && auth.startsWith("Bearer");
        boolean haveAccessToken = request.getParameter("access_token")!=null;
        return haveOauth2Token || haveAccessToken;
    }
}

Upvotes: 5

dawid gdanski
dawid gdanski

Reputation: 2452

Ok, so here is some advice for Spring beginners:

There is a FilterChainProxy class and it is worth knowing how it works. In general, every coming request is filtered by standard filters and additional ones (added in configuration).

Every WebSecurityConfigurerAdapter must have proper order and according to this order your requests are bound with proper request matcher.

The request matcher provides filters which handle your requests.

My problem was that because of improper WebAdapters ordering the AnyRequestMatcher handled my request which was not desired behaviour. The proper request matcher was at further position.

Having changed WebAdapters ordering fixed everything.

Cheers

Upvotes: 5

Related Questions