We need to set the domain of the CSRF cookie, because we have many subdomains accessing the our API under api.example.com. With the current implementation the cookie is only accessible from api.example.com, but the users will never go to this domain. They will go to x.example.com, example.com, etc...
No method to set the cookie domain.
Provided method to customize the cookie domain
4.2.2.RELEASE
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
We're experiencing the same problem.
We have api.example.com with web.example.com as the front-end, in Angular2. The Set-Cookie comes back from Spring but, being a different domain, the cookie is lost.
@renannprado , did you find another way to approach this?
@jh409 I'm no longer using this, 'cause we've changed the XSRF protection we're using.
However I guess this should help you out.
Disclaimer: I had to do some copy/paste from the original implementation 'cause there was no other way to do what I wanted.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
/**
* TODO: change the implementation when https://github.com/spring-projects/spring-security/issues/4315 is fixed
*/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${xx.csrf.cookie.domain}")
private String CSRF_COOKIE_DOMAIN;
@Value("${xx.csrf.cookie.name}")
private String CSRF_COOKIE_NAME;
@Autowired
private Environment environment;
@Override
protected void configure(HttpSecurity http) throws Exception {
final CsrfConfigurer<HttpSecurity> httpSecurityCsrfConfigurer = http.csrf()
.csrfTokenRepository(new CustomDomainCookieCsrfTokenRepository(CSRF_COOKIE_DOMAIN, CSRF_COOKIE_NAME))
// the method to verify if the links are saved won't require CSRF protection
.ignoringAntMatchers("/**/xx/yy/*");
// will let the CSRF protection enabled unless we have the disable-csrf profile active
if (Arrays.stream(environment.getActiveProfiles()).anyMatch(profile -> profile.equals("disable-csrf"))) {
httpSecurityCsrfConfigurer.disable();
}
}
/**
* saveToken method had to be coped from {@link CookieCsrfTokenRepository} because the class is final
*/
public static class CustomDomainCookieCsrfTokenRepository implements CsrfTokenRepository {
private final CookieCsrfTokenRepository cookieCsrfTokenRepository;
private final String cookieDomain;
private final String cookieName;
public CustomDomainCookieCsrfTokenRepository(final String cookieDomain, final String cookieName) {
this.cookieCsrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
this.cookieCsrfTokenRepository.setCookieName(cookieName);
this.cookieDomain = cookieDomain;
this.cookieName = cookieName;
}
@Override
public CsrfToken generateToken(final HttpServletRequest request) {
return cookieCsrfTokenRepository.generateToken(request);
}
@Override
public void saveToken(final CsrfToken token, final HttpServletRequest request, final HttpServletResponse response) {
String tokenValue = token == null ? "" : token.getToken();
Cookie cookie = new Cookie(cookieName, tokenValue);
cookie.setSecure(request.isSecure());
cookie.setDomain(cookieDomain);
if (!StringUtils.isEmpty(this.cookieCsrfTokenRepository.getCookiePath())) {
cookie.setPath(this.cookieCsrfTokenRepository.getCookiePath());
} else {
cookie.setPath(this.getRequestContext(request));
}
if (token == null) {
cookie.setMaxAge(0);
}
else {
cookie.setMaxAge(-1);
}
// we don't need this because our cookie won't be http only
// if (cookieHttpOnly && setHttpOnlyMethod != null) {
// ReflectionUtils.invokeMethod(setHttpOnlyMethod, cookie, Boolean.TRUE);
// }
response.addCookie(cookie);
}
@Override
public CsrfToken loadToken(final HttpServletRequest request) {
return cookieCsrfTokenRepository.loadToken(request);
}
private String getRequestContext(HttpServletRequest request) {
String contextPath = request.getContextPath();
return contextPath.length() > 0 ? contextPath : "/";
}
}
}
Any thoughts on this from the spring team? This would be useful to us as well.
Any updates on this?
We don't have any plans for implementing this ourselves a the moment. However, if someone wants to provide a PR that would be very welcome.
@rwinch I'd like to take it
Most helpful comment
Any thoughts on this from the spring team? This would be useful to us as well.