0

I have been working at this for the last 7 hours and cannot seem to make any headway. I am trying to get my XSRF-TOKEN available to the frontend (Angular 6). However, it seems that it is never available. When I perform analysis in the network tab and look under "Cookie" it appears to be there in all its shining glory. However, if I use Angular's CookieService and attempt to retrieve it nothing comes back.

Things to keep in mind:

  1. When run on my local machine I am able to get the XSRF-TOKEN out and place it in future header requests.
  2. The issue occurs when the two frontend and backend (Spring-boot) are hosted on two different hosts but under the same domain. For example: Frontend could be frontend.home.com; Backend could be backend.home.com.
  3. HttpOnly is set to False.
  4. Angular's withCredentials property is set to true as well.

I would really appreciate any suggestions on how to proceed with this seemingly trivial yet frustrating issue.

Thanks.

Backend:

http.httpBasic()
    .authenticationEntryPoint(new AuthenticationFailureHandler())
    .and()
        .authorizeRequests()
        .antMatchers("List of URL").permitAll()
        .anyRequest().authenticated()
    .and()
        .csrf()
        .ignoringAntMatchers("List of API to ignore CSRF Token")
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .and()
        .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
http.logout()
    .permitAll()
    .logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
}

@Autowired
private CsrfTokenRepository csrfTokenRepository() {
    HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
    repository.setHeaderName("X-XSRF-TOKEN");
    return repository;
}

Frontend:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let newHeaders: HttpHeaders = req.headers;

    let csrfToken = this.cookieService.get("XSRF-TOKEN");
    if (csrfToken === null || csrfToken === undefined) {
        newHeaders = newHeaders.append('X-XSRF-TOKEN', 'undefined');
    } else {
        newHeaders = newHeaders.append('X-XSRF-TOKEN', csrfToken);
    }

    const authReq = req.clone({headers: newHeaders, withCredentials: true});
    return next.handle(authReq);
}

Also, when I look at the developer tools I see three different domains.

  1. home.com
  2. backend.home.com (XSRF cookie is only found here and not accessible elsewhere)
  3. frontend.home.com
ButtahNBred
  • 432
  • 1
  • 8
  • 24
  • Can you give us a hint what the cookie domain of the cookies is (they're set by the server, right?)? It sounds like the backend sends the cookie, but only for backend.home.com, therefore the cookie is not available from frontend.home.com. – Florian Apr 22 '20 at 20:53
  • Yes, you are correct. I viewed the cookies for backend.home.com and it is present there and not present in the frontend.home.com. I'm just not sure why the cookie is not being sent. – ButtahNBred Apr 22 '20 at 21:09
  • For that we would need to see the code you're using to send the code. If it's a framework, out can probably configure the cookie domain. If you're doing it on your own, it would probably a change in your code. Please show us the relevant code/information on where you send the cookie. – Florian Apr 22 '20 at 21:44
  • I'm also not quite sure, if you even need to put the XSRF header on your own. I don't know how spring really works there, but if the cookie is set on the `backend.home.com` domain, it should also be transferred (the cookie) when you request a URL from angular (or any other JavaScript). But like I said, that is only guessing :P – Florian Apr 23 '20 at 13:38

1 Answers1

0

Based on the edits and the comments, you probably need to change the Cookie Domain of the CSRF cookie to be set on the home.com domain, instead of on the backend.home.com domain only. You can achieve that by changing the CookieCsrfTokenRepository:

CookieCsrfTokenRepository repository = CookieCsrfTokenRepository.withHttpOnlyFalse();
repository.setCookieDomain("home.com");

http.httpBasic()
    .authenticationEntryPoint(new AuthenticationFailureHandler())
    .and()
        .authorizeRequests()
        .antMatchers("List of URL").permitAll()
        .anyRequest().authenticated()
    .and()
        .csrf()
        .ignoringAntMatchers("List of API to ignore CSRF Token")
        .csrfTokenRepository(repository)
    .and()
        .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
http.logout()
    .permitAll()
    .logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)));
}

See the first two lines where the repository is setup like I wrote above and then used as the token repository.

Florian
  • 2,796
  • 1
  • 15
  • 25