Describe the bug I am working on a small project to have a 'gateway' application in between Landing page and Backend API servers. Following the admin guide of Spring boot 3.2.0 that I'm currently using in my project I have created the following class for the security filter:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        LOGGER.info("<================   securityFilterChain =================>");

        http
//       NO DIFFERENCE         .cors(Customizer.withDefaults())
//       NO DIFFERENCE          .cors((cors) -> cors
//       NO DIFFERENCE                  .configurationSource(corsConfigurationSource()))
//       NO DIFFERENCE                 .cors(cors -> cors.configurationSource(request -> {
//                            CorsConfiguration configuration = new CorsConfiguration();
//                            configuration.setAllowedOrigins(Arrays.asList(landing, gateway, php));
//                            configuration.addAllowedHeader("Authorization, Origin, X-Requested-With, Content-Type, Accept, cache-control");
                            configuration.addAllowedMethod("HEAD, GET, PUT, POST, OPTIONS");
                            configuration.addExposedHeader("Authorization, Cache-Control");
                            configuration.setAllowCredentials(true);
                            return configuration;

                }))

                .addFilterBefore(corsFilter(), CorsFilter.class)
                .authorizeRequests(authorize -> authorize
                        .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        .requestMatchers("/info/**").permitAll()
                        .requestMatchers("/register/**").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oAuth -> oAuth.jwt(jwt -> {
                    jwt.decoder(jwtDecoder());
                    jwt.jwtAuthenticationConverter(jwtAuthConverter);
                }))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults())
                .csrf(Customizer.withDefaults())
                ;

        return http.build();
    }

    @Bean
    public CorsFilter corsFilter() {
        CorsFilter corsFilter = new CorsFilter(corsConfigurationSource());
        return corsFilter;
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        LOGGER.info("<================  CORS config load =================>");

        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList(landing, gateway, php));
        configuration.addAllowedHeader("Authorization, Origin, X-Requested-With, Content-Type, Accept, cache-control");
        configuration.addAllowedMethod("HEAD, GET, PUT, POST, OPTIONS");
        configuration.addExposedHeader("Authorization, Cache-Control");
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

Commented-out lines make no difference to the errors I'm having with this setup.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://gateway/. host.com/biosamples/storage/count. (Reason: CORS header 'Access-Control-Allow-Origin' missing). Status code: 403.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://gateway/. host.com/api/v0.0.3/visits/count. (Reason: CORS header 'Access-Control-Allow-Origin' missing). Status code: 403.

To Reproduce I cannot share any of my projects but my Gateway is a Spring boot Java application, my Landing server is React.js and my backends API servers are PHP. So without GW in between everything works fine, Gateway itself can call API and get the data from backend over Poastman, however, the Landing page is unable to get anything but login functionality.

Expected behavior As the config shows that the AllowedOrigins is provided, however on the errors we see that it isn't.

But if I will add this lines to the HTTP config: http.headers(header -> header.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", landing)));

Then the error changed to: CORS Missing Allow Credentials

Furthermore, if I add http.headers(header -> header.addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true"))); then the error is: CORS PreFlight is not Allowed

In the config and documentation is clearly states that it should allow preflight and Allowed Origin, but it is not.

I would like to have some explanations on this one as it seems that either the documentation is incorrect or the CorsConfigurationSource doesn't work the way it should.

Thanks

Comment From: sjohnr

@chuklov, thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add a minimal sample that reproduces this issue if you feel this is a genuine bug.

Comment From: chuklov

@sjohnr I can show you the minimal sample with the provided above code that is minimal and fully shown. However, I cannot set up 4 servers in a private cloud and give public access to the data that is private. You can contact me in a private message and I will give you access over the remote session or can give a link with the user details to log in and see the errors on the system. But the initial code that doesn't work is already provided and I showed errors that prove the code and the documentation are incorrect, how come this is an StackOverflow question if the docs are incorrect? Moreover I do have this question open in SO: https://stackoverflow.com/questions/78163147/cors-with-spring-security-v3-2-0

Therefore, can you please contact me in order to resolve this unworking situation? BR

Comment From: sjohnr

@chuklov I'm sorry your having trouble.

But the initial code that doesn't work is already provided

Your configuration is neither minimal, nor according to the documentation.

I showed errors that prove the code and the documentation are incorrect

I have used the documentation to test CORS myself and verified that it does work.

I cannot set up 4 servers in a private cloud and give public access to the data that is private.

There is no need to include multiple services in a sample to reproduce CORS issues as it is between a browser client and a server only. Please provide a minimal sample that reproduces your issue for this to be considered further. Most likely, doing so will help you find your issue.

Comment From: gulybyte

@chuklov Did you manage to solve the problem? If so, how?

I went through a similar situation. In my case, I solved it by simply adding withDefaults() from org.springframework.security.config.Customizer.withDefaults as a parameter in cors(). In short, delete the corsFilter() function, keep corsConfigurationSource() as it is, and remove all CORS configurations in your securityFilterChain(), making it look like this:

public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

    http
        .cors(withDefaults())
        // other configurations ...

    return http.build();
}

A simplified version with Basic Authorization:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
            .cors(withDefaults())
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .httpBasic(withDefaults())
            .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        var userDetails = User.builder()
            .username("anyuser")
            .password("{bcrypt}$2a$10$/ykN/uuXUQ4yKmh7D2o9du2tIkFeOhdUU47z0QiggTVtzDw19B3fO") // password: 1234
            .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource(){

        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*")); // insert your domains here, * leaves public access
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH"));
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "content-type"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

If you haven't solved it yet, try what I mentioned. I saw that you left in your code the // NO DIFFERENCE .cors(Customizer.withDefaults()) but still, try again the way I mentioned and let me know if it worked or not.