Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cors reference documentation seems incomplete/innacurate #14852

Open
xenoterracide opened this issue Apr 5, 2024 · 5 comments
Open

cors reference documentation seems incomplete/innacurate #14852

xenoterracide opened this issue Apr 5, 2024 · 5 comments
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug

Comments

@xenoterracide
Copy link

xenoterracide commented Apr 5, 2024

I've lost my mind trying things here. This is only my latest attempt, I've basically been in "add" mode.

The documentation suggests this should be enough.

https://docs.spring.io/spring-security/reference/servlet/integrations/cors.html

// © Copyright 2024 Caleb Cushing
// SPDX-License-Identifier: AGPL-3.0-or-later

package com.xenoterracide.controller.authn;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@EnableWebSecurity
@SpringBootApplication
public class ResourceServer {

  ResourceServer() {}

  public static void main(String[] args) {
    SpringApplication.run(ResourceServer.class, args);
  }

  @Bean
  @Order(0)
  SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
      .securityMatcher("/**")
      .cors(cors -> cors.configurationSource(this.corsConfigurationSource()))
      .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
      .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
      .httpBasic(c -> c.disable())
      .formLogin(f -> f.disable());
    return http.build();
  }

  @Bean
  WebSecurityCustomizer webSecurityCustomizer() {
    return web -> web.debug(true);
  }

  @Bean
  HttpExchangeRepository httpExchangeRepository() {
    return new InMemoryHttpExchangeRepository();
  }

  @Bean
  CorsConfigurationSource corsConfigurationSource() {
    var cors = new CorsConfiguration();
    cors.addAllowedOrigin("http://localhost:3000");
    cors.addAllowedMethod("*");
    cors.addAllowedHeader("*");
    var source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", cors);
    return source;
  }

  @Bean
  WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
      @Override
      public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/external").allowedOrigins("http://localhost:3000");
      }
    };
  }

  @RestController
  static class OidcTestController {

    private final Logger log = LogManager.getLogger(this.getClass());

    @CrossOrigin(originPatterns = "*")
    @GetMapping("/api/external")
    @NonNull
    String index(@Nullable Authentication details) {
      this.log.info("{}", details);
      var name = details != null ? details.getName() : "world";
      return "Hello, " + name;
    }
  }
}
 INFO 743291 - Spring Security Debugger                                     : 

************************************************************

Request received for GET '/api/external':

org.apache.catalina.connector.RequestFacade@20882dd6

servletPath:/api/external
pathInfo:null
headers: 
host: localhost:8080
user-agent: curl/8.6.0
accept: */*


Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter
  HeaderWriterFilter
  CorsFilter
  CsrfFilter
  LogoutFilter
  BearerTokenAuthenticationFilter
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  ExceptionTranslationFilter
  AuthorizationFilter
]


************************************************************


DEBUG 743291 - o.spri.secu.web.FilterChainProxy                             : Securing GET /api/external
DEBUG 743291 - o.spri.secu.web.FilterChainProxy                             : Secured GET /api/external
DEBUG 743291 - o.spri.web.serv.DispatcherServlet                            : GET "/api/external", parameters={}
DEBUG 743291 - o.spri.web.serv.mvc.meth.anno.RequestMappingHandlerMapping   : Mapped to com.xenoterracide.controller.authn.ResourceServer$OidcTestController#index(Authentication)
DEBUG 743291 - o.spri.secu.web.auth.AnonymousAuthenticationFilter           : Set SecurityContextHolder to anonymous SecurityContext
 INFO 743291 - c.xeno.cont.auth.Reso.OidcTestController                     : null
DEBUG 743291 - ri.web.serv.mvc.meth.anno.RequestResponseBodyMethodProcessor : Using 'text/plain', given [*/*] and supported [text/plain, */*, application/json, application/*+json]
DEBUG 743291 - ri.web.serv.mvc.meth.anno.RequestResponseBodyMethodProcessor : Writing ["Hello, world"]
DEBUG 743291 - o.spri.web.serv.DispatcherServlet                            : Completed 200 OK
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8080...
* Connected to localhost (::1) port 8080
> GET /api/external HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.6.0
> Accept: */*
> 
< HTTP/1.1 200 
< 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
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 12
< Date: Fri, 05 Apr 2024 10:52:42 GMT
< 
{ [12 bytes data]
* Connection #0 to host localhost left intact
Hello, world
@xenoterracide xenoterracide added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Apr 5, 2024
@xenoterracide
Copy link
Author

So I finally tracked down my issue by stepping through the filter chain with the debugger... hrm... I opened another bug spring-projects/spring-framework#32580 this seems like it's a logging issue as much as anything. I don't know how I was supposed to figure out the problem was that spring was being "smart" about header output. Leaving this open in case these docs could be improved, though I'm going to speculate I would have missed it unless it was loud. I have also asked for improvements to the docs in the gs guide spring-guides/gs-rest-service-cors#39

@StevenPG
Copy link

Is there an intermediate fix for something like actuator that you've found? I've been stuck on this for hours and had to create an incredibly janky implementation for a similar issue with spring-cloud-gateway.

@xenoterracide
Copy link
Author

xenoterracide commented Apr 17, 2024

Uh, I'm not sure if I understand the question/problem you're having. I tracked mine down to a header that needed to be sent in the request that is normally sent by a web browser, but it's not generally sent from other clients unless you tell it to.

@StevenPG
Copy link

Sorry, I think I'm in the same boat. I have an empty project with spring-web-starter, spring-security-starter and a SecurityFilterChain. Following the documentation to the letter and can't make any progress into actually disabling cors successfully for RestController endpoints or actuator endpoints.

I also just think there must be something missing from the documentation, and can't figure out a way around it.

@xenoterracide
Copy link
Author

Sounds like the inverse of my problem which was getting the Cors headers to actually trigger

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants