Spring Boot Webflux - Security CORS is not working - spring

I cannot seem to get CORS working right in Spring Boot's Webflux - here is my config and no matter what I do I get CORS errors with a VUE client:
#Configuration
#EnableWebFluxSecurity
class HelloWebfluxSecurityConfig {
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = listOf("http://localhost:8080")
configuration.allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", configuration)
return source
}
#Bean
fun userDetailsService(): MapReactiveUserDetailsService {
val user: UserDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("USER")
.build()
return MapReactiveUserDetailsService(user)
}
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
http
.authorizeExchange { exchanges: AuthorizeExchangeSpec ->
exchanges
.anyExchange().authenticated()
}
.httpBasic(withDefaults())
.formLogin(withDefaults())
.csrf().disable()
.cors().configurationSource(corsConfigurationSource())
return http.build()
}
}
I've tried cors().configurationSource(withDefaults()) too (which should use the configuration source bean I've defined, according to the docs.
What do I need to do to make this work?
EDIT: Here's my browser error:
Access to XMLHttpRequest at 'http://localhost:8088/data/configuration' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

So, it turns out that I needed to add:
configuration.allowedHeaders = listOf("*")
Anybody that's having problems with this can add this to application.properties to see the exact reason that the request is rejected (or set your debugger to debug in the DefaultCorsProcessor class) and watch what happens:
logging.level.org.springframework.web.cors.reactive.DefaultCorsProcessor=debug
... o.s.w.c.reactive.DefaultCorsProcessor : Reject: headers '[authorization]' are not allowed

In Rest controller you could do this:
#RestController
#CrossOrigin(origins = "*")
for webflux look at this:
Enable CORS in Spring 5 Webflux?

Related

Spring Cloud Gateway "invalid csrf token"

Spring Cloud Gateway keeps rejecting my csrf token even though request header "X-XSRF-TOKEN" and "XSRF-TOKEN" cookie are correctly set as you can see here:
This is the Spring Cloud Gateway Security configuration:
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
#Autowired
private ReactiveClientRegistrationRepository clientRegistrationRepository;
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
CorsConfiguration cors_config = new CorsConfiguration(); //Setting cors config
cors_config.setAllowCredentials(true);
cors_config.applyPermitDefaultValues();
cors_config.setAllowedOrigins(Arrays.asList("http://localhost:3000", "null"));
cors_config.setAllowedMethods(List.of("GET", "POST", "OPTIONS", "DELETE"));
cors_config.setAllowedHeaders(List.of("*"));
http.cors().configurationSource(source -> cors_config)
.and()
.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
.oauth2Login()//Setting Oauth2Login
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("http://localhost:3000/")).and()
.logout(logout -> logout //Setting Oauth2Logout
.logoutHandler(logoutHandler())
.logoutSuccessHandler(oidcLogoutSuccessHandler()))
.csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())); //Enabling csrf (all post/patch/put/... requests will need a csrf token in X-XSRF-TOKEN header
return http.build();
}
private ServerLogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler =
new OidcClientInitiatedServerLogoutSuccessHandler(this.clientRegistrationRepository);
// Sets the location that the End-User's User Agent will be redirected to
// after the logout has been performed at the Provider
oidcLogoutSuccessHandler.setPostLogoutRedirectUri("http://localhost:8090/oauth2/authorization/spring-gateway-client");
return oidcLogoutSuccessHandler;
}
private DelegatingServerLogoutHandler logoutHandler() {
//Invalidate session on logout
return new DelegatingServerLogoutHandler(
new SecurityContextServerLogoutHandler(), new WebSessionServerLogoutHandler());
}
}
Filter:
#Component
public class C {
#Bean
public WebFilter addCsrfTokenFilter() {
return (exchange, next) -> Mono.just(exchange)
.flatMap(ex -> ex.<Mono<CsrfToken>>getAttribute(CsrfToken.class.getName()))
.doOnNext(ex -> {
})
.then(next.filter(exchange));
}
}
I don't really know how to solve this.

Allow origins in Spring 5.7.3

I'm lost with CORS again :(
I followed docs and set up my WebConfig like this:
#EnableWebSecurity
class WebSecurityConfig() {
#Bean
fun corsConfigurationSource(): CorsConfigurationSource = CorsConfiguration()
.apply { allowedOrigins = listOf("http://localhost:3000", "*") }
.apply { allowedMethods = listOf("*") }
.let { corsConfig ->
UrlBasedCorsConfigurationSource().apply { registerCorsConfiguration("/**", corsConfig) }
}
#Bean
fun configure(http: HttpSecurity): SecurityFilterChain {
http
.csrf().disable()
.cors { }
http.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
http
.authorizeRequests()
.antMatchers(
"/auth/**",
"**/demo",
).permitAll()
.anyRequest().authenticated()
http.exceptionHandling { it.authenticationEntryPoint { _, response, authException ->
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.message)
} }
http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter::class.java)
return http.build()
}
}
and I still get CORS error, "http://localhost:3000" being blocked. I tried multiple variations, followed multiple tutorials and I just can't solve this one.
Did I make and error that I can't see?
Edit: When I "solved" this I encountered another irrational problem. Apparently the core problem was in editor/browser. After restart everything works as expected.
Guys, if you think something should work and it doesn't, just try to restart editor and browser. It may save you 2 days.
I'm going to add that CorsConfigurationSource can be replaced by CorsFilter bean.
fun corsFilter() : CorsFilter
fun corsConfigurationSource() : CorsConfigurationSource
It's really strange but after adding allowedHeaders to CorsConfiguration it works fine:
CorsConfiguration()
.apply {
allowedOrigins = listOf("http://localhost:3000")
allowedMethods = listOf("*")
allowedHeaders = listOf("*")
}
I know it's weird because spring doesn't mention allowedHeaders in their docs, but I verified this solution both on my localhost and AWS deploy and in both cases I get cors error if I omit allowedHeaders.
Very good tip is also to check Disable cache checkbox in your DevTools -> Network tab when testing your cors implementation.
To make it complete, cors can be configured by implementing of one of two beans:
fun corsFilter() : CorsFilter
fun corsConfigurationSource() : CorsConfigurationSource

CORS header ‘Access-Control-Allow-Origin’ missing with Spring Data REST

I'm trying to solve a CORS issue with spring data rest but seems like the CORS headers are not attached. This is the config I have:
#Component
class DataRestConfig: RepositoryRestConfigurer {
override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration?, cors: CorsRegistry?) {
cors?.addMapping("/*")
?.allowedOrigins("*")
?.allowedMethods("GET", "PUT", "DELETE","PATCH","POST","OPTIONS")
}
}
I also had the same issue with other API routes that are out of spring data rest. Here is my WebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
open class WebSecurityConfig(private val userDetailsServices: DatabaseUserDetailsServices, private val jwtService: JWTService): WebSecurityConfigurerAdapter() {
#Value("\${auth.jwt.secret}")
private var secret: String = ""
override fun configure(http: HttpSecurity) {
http
.cors().and()
.csrf().disable()
.addFilterAfter(JWTAuthorizationFilter(userDetailsServices, secret, jwtService),UsernamePasswordAuthenticationFilter::class.java)
.authorizeRequests()
.antMatchers(HttpMethod.POST,UserController.LOGIN_URL).permitAll()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
}
}
Edit:
Added the full WebSecurityConfigurerAdapter
I noticed that the OPTIONS request gets 403 this is why I've added the antMatchers for OPTIONS method but it did not help.
Here are the response and request headers. There is no response body:
If using Spring MVC you should configure the CORS behavior like so
#Configuration
public class CorsConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS");
}
}
I don't know why the other configs are not taken into account and I don't know if this is considered a good solution but since I only need this on the local environment it is not that important. This is how I got this working:
#Bean
#Profile("local")
open fun corsConfigurationSource(): CorsConfigurationSource{
val cors = UrlBasedCorsConfigurationSource()
val config = CorsConfiguration().applyPermitDefaultValues()
config.addAllowedMethod(HttpMethod.OPTIONS)
config.addAllowedMethod(HttpMethod.POST)
config.addAllowedMethod(HttpMethod.PATCH)
config.addAllowedMethod(HttpMethod.DELETE)
cors.registerCorsConfiguration("/**", config)
return cors
}
You can always have a CorsFilter to modify response headers. Here I have answered how we can have custom CorsFilter in Spring boot - https://stackoverflow.com/a/66882700/3709922. Kindly have a look.

App Engine Standard with Spring Boot CORS not working

I am implementing a Spring Boot application that is hosted on a Google App Engine Standard Environment.
I have configured CORS like this, following the official guide:
#Configuration
#EnableWebSecurity
class WebSecurityConfigurer : WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.csrf()
.disable()
.cors()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/api/**").permitAll()
}
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = listOf("*")
configuration.allowedMethods = listOf("GET", "POST", "OPTIONS", "PUT", "DELETE", "HEAD")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", configuration)
return source
}
executing the following cURL I receive the AllowedOrigins header as it is necessary:
curl -H "Access-Control-Request-Method: GET" -H "Origin: http://foo" -X OPTIONS "localhost:8080/api/abc/list?lang=de"
Response:
HTTP/1.1 200
Access-Control-Allow-Origin: *
Now when I have deployed my Spring App to AppEngine, I can also cURL successfully.
HTTP/2 200
access-control-allow-origin: https://myfrontend.com
access-control-allow-methods: GET
access-control-allow-credentials: true
Unfortunately, my Frontend Application gets blocked with a 403
Access to fetch at 'https://mybackend.com/api/abc/list?lang=de' from origin 'https://myfrontend.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Any tips?
Thanks.
I think you missed the important part setting headers in cors.
Add this line
configuration.allowedHeaders = listOf("*")
I used this code :
Ref : https://blogs.ashrithgn.com/disable-cors-in-spring-boot/
I have two GAE project
Python based using angular 11
Spring boot based micro services std env
Imp : No other changes in app.yaml or as mentioned by spring at
https://spring.io/guides/gs/rest-service-cors/ will make any difference
#Configuration
public class CorsConfig {
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}

CORS Origin Spring Boot Jhipster - pre-flight fails

I am using jhipster v2.27.2
I have enabled cors by uncommenting the lines in the application.yml
jhipster:
async:
corePoolSize: 2
maxPoolSize: 50
queueCapacity: 10000
cors: #By default CORS are not enabled. Uncomment to enable.
allowed-origins: "*"
allowed-methods: GET, PUT, POST, DELETE, OPTIONS
allowed-headers: "*"
exposed-headers:
allow-credentials: true
max-age: 1800
In the "WebConfigurer"
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = props.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
source.registerCorsConfiguration("/oauth/**", config);
}
return new CorsFilter(source);
}
But still when I request for the access token, I see this error
http://localhost:8080/oauth/token?username=admin&password=admin&grant_type=password&scope=read.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9090' is therefore not allowed
access. The response had HTTP status code 401.
Looks like in the default SecurityConfiguration, its not skipping security check for OPTIONS.
Try adding the following antMatcher to the protected void configure(HttpSecurity http) method in SecurityConfiguration.java
.antMatchers(org.springframework.http.HttpMethod.OPTIONS, "/api/**").permitAll()
sometimes this issue will come if you forget to register cors on specified URLs.In WebConfigurer look for corsFilter and and add these line
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
Another option in the SecurityConfiguration.java. Instead of using antMatcher within the configure(HttpSecurity) override, is to add it within the configure(WebSecurity) override...
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")

Resources