Front end : React 16.12.0 | Back end : Spring 2.2.4.RELEASE
I am currently facing an issue regarding preflight CORS request. From my understanding, each non simple request (eg. GET with Authorization token) will trigger a preflight CORS request that the server has to validate through a response with all allowed parameters the "real" request send after should respect.
The preflight is coming on server side as a OPTION request and should hit my cors filter in order to be validated.
Request send from front end:
export function greeting() {
const access_token = AuthTokenFromStore().oauthData.access_token;
const obj = {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Accept': 'application/json',
'Authorization': 'Bearer ' + access_token
}
};
return (fetch('http://localhost:8080/api/v1/greeting', obj)
.then(res => res.json()));
}
Error message receive:
OPTIONS http://localhost:8080/api/v1/greeting 401
Access to fetch at 'http://localhost:8080/api/v1/greeting' from origin 'http://localhost:3000' 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.
spring security config:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityServerConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.cors() // <-- fetch the corsFilter bean
.and()
.csrf().disable()
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/oauth/authorize**", "/login**", "/error**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
//...
}
cors filter config:
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSConfig {
@Bean
public CorsFilter corsFilter() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("origin", "x-authorization", "content-type", "accept"));
configuration.setMaxAge(3600L);
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return new CorsFilter(source);
}
}
my log :
2020-03-19 17:00:24.993 DEBUG 9452 --- [nio-8080-exec-2] o.a.coyote.http11.Http11InputBuffer : Received [OPTIONS /api/v1/greeting HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:3000
Sec-Fetch-Dest: empty
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Access-Control-Request-Headers: authorization
Accept: */*
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
]
2020-03-19 17:00:24.994 DEBUG 9452 --- [nio-8080-exec-2] o.a.c.authenticator.AuthenticatorBase : Security checking request OPTIONS /api/v1/greeting
2020-03-19 17:00:24.994 DEBUG 9452 --- [nio-8080-exec-2] org.apache.catalina.realm.RealmBase : No applicable constraints defined
2020-03-19 17:00:24.994 DEBUG 9452 --- [nio-8080-exec-2] o.a.c.authenticator.AuthenticatorBase : Not subject to any constraint
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token']
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/greeting'; against '/oauth/token'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/token_key']
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/greeting'; against '/oauth/token_key'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/oauth/check_token']
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/greeting'; against '/oauth/check_token'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 1 of 10 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 2 of 10 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 3 of 10 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 4 of 10 in additional filter chain; firing Filter: 'LogoutFilter'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', GET]
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /api/v1/greeting' doesn't match 'GET /logout'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', POST]
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /api/v1/greeting' doesn't match 'POST /logout'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', PUT]
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /api/v1/greeting' doesn't match 'PUT /logout'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : Trying to match using Ant [pattern='/logout', DELETE]
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'OPTIONS /api/v1/greeting' doesn't match 'DELETE /logout'
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.web.util.matcher.OrRequestMatcher : No matches found
2020-03-19 17:00:25.000 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 5 of 10 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
2020-03-19 17:00:25.001 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.o.p.a.BearerTokenExtractor : Token not found in headers. Trying request parameters.
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] org.apache.tomcat.util.http.Parameters : Set encoding to UTF-8
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.o.p.a.BearerTokenExtractor : Token not found in request parameters. Not an OAuth2 request.
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] p.a.OAuth2AuthenticationProcessingFilter : No token in request, will continue chain.
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 6 of 10 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 7 of 10 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 8 of 10 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 9 of 10 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : /api/v1/greeting at position 10 of 10 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : '/api/v1/greeting'; against '/api/**'
2020-03-19 17:00:25.006 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /api/v1/greeting; Attributes: [#oauth2.throwOnError(authenticated)]
2020-03-19 17:00:25.010 DEBUG 9452 --- [nio-8080-exec-2] o.s.s.w.a.ExceptionTranslationFilter : Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
During debug, i can see http.cors() get my bean corsFilter properly but why it does not appear in the log ?
I tested a lot of solution (google reasearcg, tutorials, official spring docs, baeldung, stackoverflow... etc) no one worked for me. Also, the framework gives us a way to do it properly so i want to avoid adding filter manually to bypass OPTIONS request (it will be my last resort).
Could someone help me on this issue ? or point out what i did wrong ?
Thanks for your help !