Spring boot REST service options 401 on OAuth /token
Asked Answered
A

4

14

I'm using Spring Boot to make a simple rest service. To consume it in Angular 2, I've got CORS problem when retrieving token on OAuth /token endpoint.

The error message in Chrome is below.

error message:
error message

zone.js:101 OPTIONS http://192.168.0.9:8080/api/oauth/token 
XMLHttpRequest cannot load http://192.168.0.9:8080/api/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 401.

The related files are below.

MyConfig.java

@Configuration
public class MyConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("**")
                        .allowedOrigins("*").allowedMethods("POST, GET, HEAD, OPTIONS")
                .allowCredentials(true)
                .allowedHeaders("Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers")
                .exposedHeaders("Access-Control-Allow-Origin,Access-Control-Allow-Credentials")
                .maxAge(10);
            }
        };
    }
}

OAuth2ResourceServerConfig.java

@Configuration
@EnableResourceServer
class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .anonymous()
            .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS,"**").permitAll()
                .antMatchers("/authenticated/**").authenticated()
                ;
    }

}

I'm new to Java and spring. I found some similar question, such as OAuth2 - Status 401 on OPTIONS request while retrieving TOKEN, but I really don't understand how to make it work in Spring Boot.

Please note normal rest controller endpoint works fine. The problem is OAuth /token, the options request returns 401 status.

Alissaalistair answered 30/5, 2016 at 2:7 Comment(0)
U
24

You can add this CORS Filter to your project

    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)

    public class SimpleCORSFilter implements Filter {

    @Override
    public void init(FilterConfig fc) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) resp;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, resp);
        }

    }

    @Override
    public void destroy() {
    }

 }
Unaneled answered 30/5, 2016 at 3:56 Comment(4)
I did find this answer in other post. But in Spring Boot, it recommended using WebMvcConfigurerAdapter as shown in my code rather than filter. You can see they have similar code except "OPTIONS" part. So I wonder if possible to add similar "OPTIONS" code to my file? Thanks.Alissaalistair
Thank you so much!!!!! You saved my day after I've spent about 4 days with this problem!!!Mohler
this solution works but didn't answer why the original question doesn't work though.Performance
I had an issue where some times cors worked other times do not. I think Spring changes the order dynamically so I just had to add @Order(Ordered.HIGHEST_PRECEDENCE) so it was always the first priority.Serration
R
7

if you are using spring boot + Spring OAuth you must add

@Order(Ordered.HIGHEST_PRECEDENCE)

to your CORS filter

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {

    private FilterConfig config;

    public static final String CREDENTIALS_NAME = "Access-Control-Allow-Credentials";
    public static final String ORIGIN_NAME = "Access-Control-Allow-Origin";
    public static final String METHODS_NAME = "Access-Control-Allow-Methods";
    public static final String HEADERS_NAME = "Access-Control-Allow-Headers";
    public static final String MAX_AGE_NAME = "Access-Control-Max-Age";

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) resp;
        HttpServletRequest request = (HttpServletRequest) req;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, resp);
        }

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        config = filterConfig;
    }
}
Rusty answered 25/11, 2017 at 12:29 Comment(0)
C
2

Here is native Spring Framework solution:

@Bean
public CorsFilter corsFilter(CorsConfiguration config) {
    log.debug("Registering CORS filter");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/api/**", config);
    source.registerCorsConfiguration("/v2/api-docs", config);
    source.registerCorsConfiguration("/oauth/**", config);
    return new CorsFilter(source);
}
Carpogonium answered 17/1, 2017 at 21:6 Comment(0)
E
1

The browser checks CORS settings via a request with OPTIONS header. And if you've configured authorization, OPTIONS request will be blocked as unauthorized.

You simply can add cors support in WebConfigurerAdapter.

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ...
        http.cors();
    }
}

Check this link for more info: https://www.baeldung.com/spring-security-cors-preflight

Edraedrea answered 30/3, 2020 at 15:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.