spring-bootazure-java-sdkspring-cloud-azure

Spring Boot 3.0 with Azure AAD Security and CORS not working, worked in Spring Boot 2.6


For our application we had to write a custom OIDCUserService and OIDCUser objects as well as customize Azure's AAD Security Config with Spring.

Here is the code for Spring Boot 2.6 that works.

@EnableWebSecurity
class AADServerConfig {

@Order(1)
@Configuration
static class ApiWebSecurityConfigurationAdapter extends AadResourceServerWebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http)
        http
                .cors()
                .and()
                .csrf().disable()
                .antMatcher("/api/**")
                .authorizeRequests().anyRequest().authenticated()

        http
                .headers()
                .frameOptions()
                .disable()
                .httpStrictTransportSecurity()
                .disable()
    }
}

@Configuration
class HtmlWebSecurityConfigurerAdapter extends AadWebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
    @Autowired
    private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
    @Autowired
    private OAuth2HttpCookieAuthorizationRequestRepository oAuth2HttpCookieAuthorizationRequestRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http)
        http
                .oauth2Login()
                .authorizationEndpoint()
                .authorizationRequestRepository(oAuth2HttpCookieAuthorizationRequestRepository)
                .and()
                .successHandler(oAuth2AuthenticationSuccessHandler)
                .failureHandler(oAuth2AuthenticationFailureHandler)

        http
                .cors()
                .and()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/login", "/*.js", "/*.css", "/token", "/actuator/**", "/actuator/prometheus", "/version/**", "/docusign/events/**").permitAll()
                .anyRequest().authenticated()

        http
                .headers()
                .frameOptions()
                .disable()
                .httpStrictTransportSecurity()
                .disable()
    }
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration cors = new CorsConfiguration()
    cors.setAllowedOrigins([
            "http://localhost:3000",
            "http://localhost:3001",
            "http://localhost:8080",
            "https://localhost:8080",
            "https://dev-local.panoram.co"
    ])
    cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "FETCH", "PATCH"))
    cors.setAllowCredentials(true)
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource()
    source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues())
    return source
}
}

All fine and good. We now upgrade to Spring Boot 3.x and Azure 5.4 and here is the code like above, but refactored to match the new way to config Spring Security etc. Now CORS is not working. The big difference I see is before we extended AadResourceServerWebSecurityConfigurerAdapter in 2.6 but that no longer exists in Azure 5.4 and we have to use AadResourceServerHttpSecurityConfigurer.

@EnableWebSecurity
class AADServerConfig {

@Order(1)
@Configuration
//com.azure.spring.cloud.autoconfigure.aad.AadResourceServerWebSecurityConfigurerAdapter
class ApiWebSecurityConfigurationAdapter extends AadResourceServerHttpSecurityConfigurer {
    @Override
    void configure(HttpSecurity http) throws Exception {
        super.configure(http)
        http
                .cors(corsCustomizer -> {
                    corsCustomizer.configurationSource(corsConfigurationSource())
                })
                .csrf(csrfConfig -> {
                    csrfConfig.disable()
                })
                .securityMatcher("/api/**")
                .authorizeHttpRequests {}(requestsCustomizer -> {
                    requestsCustomizer.anyRequest().authenticated()
                })

        http
                .headers(header -> {
                    header.frameOptions {frameOptions -> {
                        frameOptions.disable()
                    }}
                    header.httpStrictTransportSecurity {transportConfig ->
                        transportConfig.disable()
                    }
                })
    }
}

@Configuration
class HtmlWebSecurityConfigurerAdapter extends AadWebApplicationHttpSecurityConfigurer {
    @Autowired
    private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
    @Autowired
    private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
    @Autowired
    private OAuth2HttpCookieAuthorizationRequestRepository oAuth2HttpCookieAuthorizationRequestRepository;

    @Override
    void configure(HttpSecurity http) throws Exception {
        super.configure(http)
        http
                .oauth2Login(customizer -> {
                    customizer.authorizationEndpoint {endpointConfig -> {
                        endpointConfig.authorizationRequestRepository(oAuth2HttpCookieAuthorizationRequestRepository)
                    }}
                    customizer.successHandler {oAuth2AuthenticationSuccessHandler}
                    customizer.failureHandler {oAuth2AuthenticationFailureHandler}
                })

        http
                .cors(corsCustomizer -> {
                    corsCustomizer.configurationSource(corsConfigurationSource())
                })
                .csrf(csrfConfig -> {
                    csrfConfig.disable()
                })
                .authorizeHttpRequests(authz ->{
                    authz.requestMatchers("/", "/login", "/*.js", "/*.css", "/token", "/actuator/**", "/actuator/prometheus", "/version/**", "/docusign/events/**").permitAll()
                            .anyRequest().authenticated()
                })

        http.headers(header -> {
            header.frameOptions {frameOptions -> {
                frameOptions.disable()
            }}
            header.httpStrictTransportSecurity {transportConfig ->
                transportConfig.disable()
            }
        })
    }
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration()
    configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000",
            "https://localhost:3000",
            "http://localhost:3001",
            "https://localhost:3001",
            "https://localhost:5001",
            "http://localhost:5001",
            "http://localhost:8080",
            "https://localhost:8080",
            "https://dev-local.panoram.co"))
    configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE", "OPTIONS", "FETCH", "PATCH"))
    configuration.setAllowCredentials(true)
    CorsConfigurationSource source = new UrlBasedCorsConfigurationSource()
    source.registerCorsConfiguration("/**", configuration.applyPermitDefaultValues())
    return source
}
}

I've spent about 2 weeks trying to get CORS to work again, but no luck. What am I doing wrong?

Browser Console Log


Solution

  • AadResourceServerHttpSecurityConfigurer doesn't support the configure method, could you try like this:

    class ApiWebSecurityConfig {
       @Bean
       SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
           http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                ...
    
           return http.build();
       }
    }
    
    class HtmlWebSecurityConfig {
        @Bean
        public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
            http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                ...
    
            return http.build();
        }
    }
    

    Please refer to https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/spring-security-support?tabs=SpringCloudAzure5x#spring-security-with-azure-active-directory for more info.