springspring-bootspring-securityspring-security-oauth2springdoc

Spring Authorization Server 0.3.1 CORS issue


i created an authorization server using spring-auth-server 0.3.1, and implemented the Authorization code workflow, my issue is that when my front end -springdoc- reaches the last step i get a 401 and this is what's logged into browser console :

Access to fetch at 'http://authorization-server:8080/oauth2/token' from origin 'http://client:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

i'm using spring boot 2.6.12 and here is my CORS configuration for authorization server (also copy pasted it to the client in case ):

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration {
    private final Set<String> allowedOrigins;

    @Autowired
    public WebSecurityConfiguration(
            @Value("${spring.security.cors.allowed-origins:*}") List<String> allowedOrigins) {
        this.allowedOrigins = new LinkedHashSet<>(allowedOrigins);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .csrf().disable() // without session cookies we do not need this anymore
                .authorizeRequests().anyRequest().permitAll();
        return http.build();
    }

    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        boolean useAllowedOriginPatterns = allowedOrigins.isEmpty() || allowedOrigins.contains("*");
        if (useAllowedOriginPatterns) {
            configuration.setAllowedOriginPatterns(Collections.singletonList(CorsConfiguration.ALL));
        } else {
            configuration.setAllowedOrigins(new ArrayList<>(allowedOrigins));
        }
        configuration.setAllowedMethods(Collections.singletonList(CorsConfiguration.ALL));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Collections.singletonList(CorsConfiguration.ALL));

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

and here are my security filter chain for the Auth server :

    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http.formLogin(Customizer.withDefaults()).build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain standardSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                        .formLogin(Customizer.withDefaults());

        return http.build();
    }

Any idea on what i'm missing ?


Solution

  • I solved it by dropping the CorsConfigurationSource from filter chain bean and creating a filter instead.

    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class SimpleCORSFilter implements Filter {
    
        private final Set<String> allowedOrigins;
    
        @Autowired
        public SimpleCORSFilter(@Value("${spring.security.cors.allowed-origins:*}") Set<String> allowedOrigins) {
            this.allowedOrigins = allowedOrigins;
        }
    
        @Override
        public void init(FilterConfig fc) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse resp,
                             FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) resp;
            HttpServletRequest request = (HttpServletRequest) req;
            String origin = request.getHeader("referer");
            if(origin != null ){
                Optional<String> first = allowedOrigins.stream().filter(origin::startsWith).findFirst();
                first.ifPresent(s -> response.setHeader("Access-Control-Allow-Origin", s));
            }
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
    
            if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            } else {
                chain.doFilter(req, resp);
            }
        }
    
        @Override
        public void destroy() {
        }
    
    }