gorecode
gorecode

Reputation: 1

Global CORS configuration not working - Invalid CORS Request

I have problems implementing CORS in my project. Maybe someone here can help me. Problem: I receive the response "Invalid CORS Request" with my implementation of CORS.

What does my implementation look like? This is my SecurityFilterChain:

  @Bean
  public SecurityFilterChain oidcFilterChain(final HttpSecurity http) throws Exception {
    return http
        .cors(withDefaults())
        .securityMatcher(new OrRequestMatcher(
            new AntPathRequestMatcher(ApplicationConfig.BASE_PATH + "/**")))
        .csrf(AbstractHttpConfigurer::disable)
        .authorizeHttpRequests(authorize -> authorize
            .anyRequest().authenticated())
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
        .build();
  }

and this is my corsConfigurationSource():

@Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource() {
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

      // Create a new CORS configuration
      CorsConfiguration config = new CorsConfiguration() {
        {
          setAllowedMethods(List.of(
              HttpMethod.GET.name(),
              HttpMethod.HEAD.name(),
              HttpMethod.POST.name(),
              HttpMethod.PUT.name(),
              HttpMethod.DELETE.name(),
              HttpMethod.OPTIONS.name()));
        }

        // Check if the request origin is allowed
        @Override
        public String checkOrigin(String requestOrigin) {
          if (requestOrigin == null || requestOrigin.isEmpty()) {
            return null;
          }
          String originToCheck = trimTrailingSlash(requestOrigin);

          try {
            // Get the current Jwt user
            Jwt jwtUser = SecurityContextUtil.getCurrentUser();

            // Get the allowed origins from the Jwt user
            List<String> allowedOrigins = jwtUser.getClaimAsStringList(ALLOWED_ORIGINS);

            if (allowedOrigins != null) {
              for (String allowedOrigin : allowedOrigins) {
                if (originToCheck.equalsIgnoreCase(trimTrailingSlash(allowedOrigin))) {
                  return requestOrigin;
                }
              }
            }

          } catch (SecurityException e) {
            log.error("SecurityException in CORS Configuration: {}", e.getMessage());
          }
          // Return null if the request origin is not allowed
          return null;
        }
      };

      // Add the CORS configuration to the source
      source.registerCorsConfiguration(ApplicationConfig.BASE_PATH + "/**", config);
      return source;
    }

The SecurityConfig.class is annotated with @EnableWebSecurity and @Configuration. Running this test:

  @Test
  @WithJwtTestUser(user = JwtTestUser.PUK_11)
  void testPreflightRequest() throws Exception {
    mockMvc.perform(options("/cadastertask/cadasters/" + CADASTER_ID_101_PREMIUM1 + "/regulations/10/tasks")
            .header("Origin", "http://localhost:8081")
            .header("Access-Control-Request-Method", "GET"))
        .andDo(print())
        .andExpect(status().isOk())
        .andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:8081"))
        .andExpect(header().string("Access-Control-Allow-Methods", "GET"))
        .andExpect(header().string("Access-Control-Max-Age", "3600"));
  }

is responding the "Invalid CORS Request" error. This is the response:

MockHttpServletRequest:
      HTTP Method = OPTIONS
      Request URI = /cadastertask/cadasters/97190101/regulations/10/tasks
       Parameters = {}
          Headers = [Origin:"http://localhost:8081", Access-Control-Request-Method:"GET"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = null
          Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Allow:"GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE, PATCH"]
     Content type = null
             Body = Invalid CORS request
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status expected:<200> but was:<403>
Expected :200
Actual   :403

While debugging I can see that the corsConfigurationSource seems to be fine, because allowed methods and origins and so on is set. But in the AbstractHandlerMapping.java class the corsInterceptor method is called and there is the config null. So it seems that my own configuration is overwritten by some other methods and I have no idea how to fix that.

Same problem exist if I change the response of my corsConfigurationSource() to the interface CorsConfigurationSource. If I use a CorsFilter it is working, but I shell use the corsConfigurationSource and not the filter.

Has anyone an idea how to fix that? It is really frustrating...

Thanks in advance.

Upvotes: 0

Views: 330

Answers (1)

CanniZarro
CanniZarro

Reputation: 63

My answer is in respect to spring security 6 (specifically 6.1.4).

Your spring security configuration class defining beans for SecurityWebFilterChain in your case SecurityFilterChain should have the cors configuration set as below:

http.cors(corsSpec -> corsSpec.configurationSource(getCorsConfigurationSource()));

where the method getCorsConfigurationSource() will look like below:

    @Bean
    CorsConfigurationSource getCorsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of(*your origings*));
        configuration.setAllowedMethods(List.of("GET", "DELETE", "PUT", "POST"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

The allowed headers, credentials and methods are set in such a way that it appeals to a general app's CORS config, please change accordingly.

Upvotes: 0

Related Questions