I have a Spring-Boot back-end and a React front-end (written in Typescript and using the Axios HTTP client). Each of these applications is hosted on a different sub-domain of a common parent-domain.
I am attempting to use cookie-based authentication for the back-end and running into the following issue:
More Context:
(with example URLs)
EXAMPLE_SESSION_ID=55A66FFAB27931F115D9E6BA23A11EE4; Max-Age=7200; Expires=Sun, 08-Nov-2020 23:53:39 GMT; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=None
Access-Control-Allow-Origin: https://ui.example.com
Access-Control-Allow-Credentials: true
Can anyone help me figure out why the cookie is not being included in requests to the back-end after the page is refreshed?
Here is the code to configure CORS and Cookies.
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
corsConfiguration.setAllowedOrigins(getAllowedOrigins());
corsConfiguration.setAllowCredentials(true);
corsConfiguration.setAllowedHeaders(
List.of(
"origin",
"content-type",
"accept",
"cookie",
"x-csrf-token"
)
);
corsConfiguration.setAllowedMethods(
List.of(
HttpMethod.OPTIONS.name(),
HttpMethod.GET.name(),
HttpMethod.POST.name(),
HttpMethod.PUT.name(),
HttpMethod.PATCH.name(),
HttpMethod.DELETE.name()
)
);
UrlBasedCorsConfigurationSource corsConfigSource = new UrlBasedCorsConfigurationSource();
corsConfigSource.registerCorsConfiguration("/**", corsConfiguration);
return corsConfigSource;
}
@Getter
@Value("${my.custom.property.session.cookie.domain:}")
private String customPropertyCookieDomain;
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> stackOverflowExampleWebServerCustomizer() {
return factory -> {
factory.addContextCustomizers(customizer -> {
Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies("None");
customizer.setCookieProcessor(cookieProcessor);
if (StringUtils.isNotBlank(customPropertyCookieDomain)) {
customizer.setSessionCookieDomain(customPropertyCookieDomain);
}
});
};
}
After spending hours staring at the "Network" tab in Google Chrome, I decided to see if the cookie I was setting would work if I tried to access the back-end directly through the browser. Sure enough, the cookie was being sent consistently even with page-refreshes and trying in new tabs. I did some digging and it turns out the issue actually had to do with the front-end. The Axios HTTP Client was not setting the withCredentials
flag after the page was refreshed. Because my CORS configuration had Access-Control-Allow-Credentials
set to true, the preflight (OPTIONS) request-response didn't match the cookie, so the browser stopped appending the cookie to request headers.
This answer explains the root-cause of the problem with Axios.
When I refreshed the front-end in the browser, Axios was no longer setting withCredentials=true
.