Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS interfering with Spring Security oauth2

I'm having problems trying to get a token from oauth/token from the browser. I have a Spring Boot application with Spring Security and Spring Security oauth, and I'm trying to authenticate from a javascript SPA in a different port.

When CORS is disabled in the backend, I can get tokens from the oauth endpoints no problem, using either Postman or the terminal, but I can't get them from javascript since the CORS preflight fails.

If I enable CORS, the preflight succeeds, but now I get an InsufficientAuthenticationException saying "There's no client authentication. Try adding an appropriate authentication filter". From what I can gather, that's because Spring Security fails to get the principal from the request.

Does anyone have a suggestion on how to deal with this?

like image 937
SrThompson Avatar asked Jun 08 '17 05:06

SrThompson


2 Answers

Apparently the Oauth2 endpoints and filters get processed before getting to the Spring Security filter chain, so adding CORS filters normally wouldn't work, but adding a CORS filter bean with high order priority ended up working.

This is my dedicated configuration class for CORS (adapted from the official spring guide, I'll be tweaking it later)

@Configuration
public class CorsConfig {
//IMPORTANT: it has to be a normal configuration class, 
//not extending WebMvcConfigurerAdapter or other Spring Security class
    @Bean
    public FilterRegistrationBean customCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));

        //IMPORTANT #2: I didn't stress enough the importance of this line in my original answer, 
        //but it's here where we tell Spring to load this filter at the right point in the chain
        //(with an order of precedence higher than oauth2's filters)
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}
like image 199
SrThompson Avatar answered Oct 04 '22 20:10

SrThompson


Just an update of SrThompson's response if you are using Spring boot 2, the class FilterRegistrationBean is now generic.

So the code looks like this:

@Configuration
public class CorsConfig {

    @Bean
    public FilterRegistrationBean<CorsFilter> customCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedMethods(Collections.singletonList("*"));
        config.setAllowedHeaders(Collections.singletonList("*"));
        config.addAllowedOrigin("http://localhost:3000");
        config.addAllowedOrigin("http://production.url");
        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));

        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}
like image 27
Michael COLL Avatar answered Oct 04 '22 19:10

Michael COLL