CORS interfering with Spring Security oauth2

Tags: , , , ,



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?

Answer

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;
    }
}


Source: stackoverflow