Spring Security CORS issue only on POST requests

ghz 8months ago ⋅ 81 views

I am working on a Spring Security project with a simple authenticate (login) and register method. I know that i have set up the controllers, services and configs correctly (apart from 2 files probably) because I have tested both login and register methods with Intellij's built in HTTP Request Client which both worked as expected.

e.g. i tried my authenticate method:

POST http://localhost:8080/auth/authenticate
Content-Type: application/json

{
  "email": "user@user.at",
  "password": "user"
}

which got me a 200 http response and my token.

I then tested the same request in my browser via a firefox extension called RESTClient with the same URL and set to POST:

http://localhost:8080/auth/authenticate

and this body:

{
  "email":"user@user.at",
  "password":"user"
}

which got me a 403 forbidden error and the RESTClient giving me this response: Invalid CORS request

I then set up a test GET Method in my Controller which i was able to call in my browser with no issues. Before writing this question i had asked ChatGPT who thought the problem had something to do with CSRF protection because i can execute GET requests but no POST requests. I tried to disable CSRF in my SecurityConfiguration file but that did not help either.

for context here is my AuthenticationController class:

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthenticationController {

    private final AuthenticationService authService;

    @GetMapping("/")
    public String test() {
        return "hallo";
    }

    @PostMapping("/register")
    public ResponseEntity<AuthenticationResponse> register(@RequestBody RegisterRequest request) {
        return ResponseEntity.ok(authService.register(request));
    }

    @PostMapping("/authenticate")
    public ResponseEntity<AuthenticationResponse> authenticate(@RequestBody AuthenticationRequest request) {
        return ResponseEntity.ok(authService.authenticate(request));
    }

    @GetMapping("/getUserByToken/{token}")
    public ResponseEntity<SecureUser> getUserbyToken (@PathVariable("token") String token) {
        SecureUser secureUser = authService.getUserByToken(token);
        return secureUser==null ? new ResponseEntity<>(HttpStatus.NOT_FOUND) : ResponseEntity.ok(secureUser);
    }
}

and here is my SecurityConfiguration class which I think is most likely the source of my error:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors(cors -> {
                    cors.configurationSource(corsConfigurationSource());
                })
                .csrf()
                .disable() // Disable CSRF protection
                .authorizeHttpRequests()
                .requestMatchers("/auth/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"));
        configuration.setAllowedHeaders(List.of("Authorization"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Edit:

Here are my request and response headers of my POST request:

POST /auth/authenticate HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Content-Type: text/plain;charset=UTF-8
Content-Length: 52
Origin: moz-extension://85e222e9-b107-4f8d-86ce-6ae0f512f61a
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
HTTP/1.1 403 
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Transfer-Encoding: chunked
Date: Tue, 09 Apr 2024 10:14:43 GMT
Keep-Alive: timeout=60
Connection: keep-alive

And here is my Spring Security Trace output regarding the request:

2024-04-09T12:14:43.785+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@4884350b, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@53cba89f, org.springframework.security.web.context.SecurityContextHolderFilter@4b6942a0, org.springframework.security.web.header.HeaderWriterFilter@7c7b27c, org.springframework.web.filter.CorsFilter@66ad7167, org.springframework.security.web.authentication.logout.LogoutFilter@53b579d2, com.example.vocabtrainerapi.config.JwtAuthenticationFilter@4f571c0e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2e845f1c, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@1e9afe4e, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@14aa2123, org.springframework.security.web.session.SessionManagementFilter@15e748b5, org.springframework.security.web.access.ExceptionTranslationFilter@65c66fc4, org.springframework.security.web.access.intercept.AuthorizationFilter@d970881]] (1/1)
2024-04-09T12:14:43.785+02:00 DEBUG 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Securing POST /auth/authenticate
2024-04-09T12:14:43.785+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/13)
2024-04-09T12:14:43.786+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/13)
2024-04-09T12:14:43.786+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/13)
2024-04-09T12:14:43.786+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/13)
2024-04-09T12:14:43.786+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.security.web.FilterChainProxy        : Invoking CorsFilter (5/13)
2024-04-09T12:14:43.787+02:00 TRACE 21108 --- [nio-8080-exec-8] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]

Answers

Based on the provided information, it seems that the issue you're encountering is related to CORS (Cross-Origin Resource Sharing) configuration. The error message "Invalid CORS request" indicates that there's a problem with the CORS configuration for your Spring Security application.

Here are a few steps you can take to troubleshoot and resolve the issue:

  1. Check CORS Configuration: Ensure that your CORS configuration allows requests from the origin where your browser is running. In your SecurityConfiguration class, you've configured CORS to allow requests from http://localhost:4200. Make sure that this matches the origin from which you're making the requests in your browser.

  2. Verify Browser Requests: Double-check the origin of the requests being made from your browser. In the provided headers, the Origin header shows moz-extension://85e222e9-b107-4f8d-86ce-6ae0f512f61a, which indicates that the request is coming from a browser extension, not from http://localhost:4200 as configured in your CORS settings. Ensure that the requests are indeed originating from the correct origin.

  3. Options Requests: When making cross-origin POST requests, the browser sends a preflight OPTIONS request to check if the server allows the actual request. Ensure that your server responds correctly to OPTIONS requests with the appropriate CORS headers.

  4. Browser Developer Tools: Use your browser's developer tools to inspect the network requests and responses. Check if there are any CORS-related errors or if the CORS headers are correctly set in the server's responses.

  5. Logging: Enable debug or trace logging for Spring Security to get more detailed information about how requests are being processed. This can help identify any issues with CORS processing.

  6. Testing with Different Browsers: Test your application with different browsers to see if the issue is specific to a particular browser or extension.

By carefully examining these aspects and ensuring that your CORS configuration is correctly set up to allow requests from the appropriate origins, you should be able to resolve the "Invalid CORS request" issue and allow POST requests to be processed successfully.