3

I wish to use Spring Security (version 5.1.2) to generate a CSRF token for my Angular 7 application. I have the following in my SecurityConfig file:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and()
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

with the following RequestMapping in my controller:

@RestController
@RequestMapping("/authentication")
public class AuthenticationController {

    @GetMapping("/csrf")
    public void getCsrfToken(){...}

    @PostMapping("/register")
    public RegisterOutputDTO register(@RequestBody UserDTO input){...}
}

I gathered from various sources that the csrfTokenRepository would automatically generate a cookie with header XSRF-token on my first GET call (which is what /authentication/csrf is for), but I am not getting a cookie back from the server. Hence on my next POST call I am getting a 403 response. What could I possibly be missing?

This is my output

Babyburger
  • 1,730
  • 3
  • 19
  • 32
  • It will only create cookie `XSRF-TOKEN` if it is missing. Look at the Request Headers to see if it is already present. – Andreas Feb 26 '19 at 23:21
  • @Andreas I've thought of this as well, but I don't send any such token in the headers. I can see in the application tab of Chrome that the cookie does not exist. – Babyburger Feb 27 '19 at 16:24
  • 1
    I believe I may have found the answer. It appears that it is not possible to send a cookie cross-domain. My angular application is hosted on localhost:3000 and my Java backend is hosted on localhost:9080. It seems my options are to either deploy them on the same domain somehow or use a proxy. Source: https://stackoverflow.com/questions/48002670/angular-5-unable-to-get-xsrf-token-from-httpxsrftokenextractor – Babyburger Feb 27 '19 at 17:21

1 Answers1

1

As indicated on the comments to my question, I found the answer to my problem. A cookie can not be sent cross-domain.

My frontend was deployed on localhost:3000 and my backend on localhost:9080, which are considered different domains apparently. If I go to localhost:9080 (I get a white page, but that doesn't matter) and I then go to the application tab in Chrome, I find that the XSRF cookie I was looking for is stored like I was expecting all along. The cookie was available from the GET call I executed from my front-end. The problem is that the cookie needs to be available for localhost:3000 so that Angular can make use of the cookie.

There are multiple ways you can solve this issue in your local environment.

Use a proxy

You can use a proxy to map certain url paths to your backend. This is the solution I went with.

I run my Angular application with a webpack dev server. Webpack provides an easy way to proxy certain urls to your backend. For example:

devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 3000,
    proxy: {
        '/api': 'http://localhost:9080'
    }
}

The Angular application runs on localhost:3000. Any calls to localhost:3000/api/*. will be forwarded to localhost:9080/api/*. So in my case I no longer perform a GET call on localhost:9080/api/authentication/csrf, but I call localhost:3000/api/authentication/csrf which will then get forwarded to my backend. (I added /api to the path in my rest controller, for those wondering.)

Deploy both applications on the same port

Using the frontend-maven-plugin you can build the frontend to its dist folder and then let maven package the dist folder along with the backend for deploy. I haven't tried this, but there are various resources that show this should be easy to do with Spring boot. So both frontend and backend would be available through localhost:9080 for example.

Use Spring Profile to disable csrf locally

You can make use of Spring @Profile annotations to create a different configuration for local environment and the rest (test, acceptance, production). Csrf can simply be disabled for development. I do not prefer this option since I like to keep DEV and other environments the same as much as possible. It's also not a real answer to the problem statement.

Special thanks to the answer of user @dspies which helped me find the problem.

Babyburger
  • 1,730
  • 3
  • 19
  • 32
  • I have a very similar setup in my local too, but Spring is sending the XSRF token, and Angular is sending it back to Spring just fine. I am very confused with this answer. – Koray Tugay Nov 06 '20 at 02:40