Skip to content

RoundTable02/spring-security-corsConfigurationSource-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Security CORS Configuration Issue - Minimal Reproducible Sample

This project demonstrates a bug in Spring Security where CORS configuration silently fails when the CorsConfigurationSource bean name differs from the expected "corsConfigurationSource".

Issue Summary

When using .cors(Customizer.withDefaults()) in Spring Security configuration, the framework looks for a bean with the exact name "corsConfigurationSource". If a CorsConfigurationSource bean is registered with a different name (e.g., when using @Profile annotations with different method names), Spring Security silently falls back to DefaultCorsProcessor, which rejects all preflight requests.

Prerequisites

  • Java 17 or higher
  • Gradle (or use the wrapper: ./gradlew)

Project Structure

spring-security-cors-issue/
├── build.gradle
├── README.md
└── src/
    ├── main/
    │   ├── java/com/example/corsissue/
    │   │   ├── CorsIssueApplication.java           # Main application
    │   │   ├── config/
    │   │   │   └── SecurityConfig.java             # Bug demonstration here!
    │   │   └── controller/
    │   │       └── TestController.java             # Simple REST endpoint
    │   └── resources/
    │       └── application.yml
    └── test/
        └── java/com/example/corsissue/
            └── CorsIssueTest.java                  # Test that demonstrates the bug

How to Reproduce

Step 1: Run the Tests

./gradlew test

Step 2: Observe the Failure

The test testPreflightRequestFails() will fail because:

  1. The application is running with @ActiveProfiles("test") (non-prod profile)
  2. This activates the bean method testCorsConfigurationSource() instead of corsConfigurationSource()
  3. Spring Security's CorsConfigurer.getCorsFilter() specifically looks for a bean named "corsConfigurationSource"
  4. Since it can't find that bean, it falls back to DefaultCorsProcessor
  5. DefaultCorsProcessor rejects preflight OPTIONS requests with "Invalid CORS request" (403 Forbidden)

Step 3: Verify Expected Behavior (Production Profile)

To see that the CORS configuration works when the bean is named correctly:

  1. Open src/test/java/com/example/corsissue/CorsIssueTest.java
  2. Change @ActiveProfiles("test") to @ActiveProfiles("prod")
  3. Run the tests again

Now the preflight request should succeed because the bean is named corsConfigurationSource.

Step 4: Manual Testing (Optional)

You can also test manually using curl:

# Start the application with test profile
./gradlew bootRun --args='--spring.profiles.active=test'

# In another terminal, send a preflight request
curl -i -X OPTIONS http://localhost:8080/api/test \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: Content-Type"

You'll receive a 403 Forbidden response with the message "Invalid CORS request".

The Bug in Detail

Problem Location: SecurityConfig.java

@Bean
@Profile("!prod")
public CorsConfigurationSource testCorsConfigurationSource() {  // Wrong bean name!
    // Valid CORS configuration
    // ...
}

Why It Fails

In org.springframework.security.config.annotation.web.configurers.CorsConfigurer:

private CorsFilter getCorsFilter(ApplicationContext context) {
    // ...
    boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
    if (containsCorsSource) {
        CorsConfigurationSource configurationSource = context.getBean(
            CORS_CONFIGURATION_SOURCE_BEAN_NAME,  // Hardcoded to "corsConfigurationSource"
            CorsConfigurationSource.class
        );
        return new CorsFilter(configurationSource);
    }
    // ...
}

The constant CORS_CONFIGURATION_SOURCE_BEAN_NAME is "corsConfigurationSource". Since our test profile bean is named testCorsConfigurationSource, it's not detected.

Workarounds

Option 1: Explicitly Set Bean Name

@Bean(name = "corsConfigurationSource")
@Profile("!prod")
public CorsConfigurationSource testCorsConfigurationSource() {
    // ...
}

Option 2: Inject Configuration Explicitly

@Autowired
private CorsConfigurationSource corsConfigurationSource;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .cors(cors -> cors.configurationSource(corsConfigurationSource))
        // ...
}

Expected Behavior

One of the following should happen:

  1. Preferred: Spring Security should detect CorsConfigurationSource beans by type, not just by name
  2. Alternative: Log a clear warning or throw an exception when CORS is enabled but no valid configuration source is found
  3. Minimum: Update documentation to explicitly mention the required bean name

Related Issue

This sample project is created to demonstrate the issue reported at: [Link to GitHub issue when created]

Environment

  • Spring Boot: 3.2.0
  • Spring Security: (included with Spring Boot)
  • Java: 17
  • Build Tool: Gradle

License

This is a minimal reproducible sample for demonstration purposes only.

About

CORS configuration silently fails when CorsConfigurationSource bean name differs from default "corsConfigurationSource"

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages