Angular 6 does not add X-XSRF-TOKEN header to http request

44,487

Solution 1

The problem once again is Angular's poor documentation.

The fact is, Angular will add the X-XSRF-TOKEN header only if the XSRF-TOKEN cookie was generated server-side with the following options:

  • Path = /
  • httpOnly = false (this is very important, and fully undocumented)

Besides, the Angular app and the URL being called must reside on the same server.

Refer this Angular Github issue

Solution 2

On my team, the problem was that we were using an absolute path instead of a relative path.

So do not use an absolute path like:

this.http.post<any>("https://example.com/api/endpoint",data)

Use

this.http.post<any>("api/endpoint",data)

Or use

this.http.post<any>("//example.com/api/endpoint",data)

This is because absolute paths are explicitly ignored by Angular code on HttpClientXsrfModule (see)

Solution 3

After struggling for countless hours, the solution that worked for us was changing the request (in Angular) from 'https://example.com' to '//example.com'.

None of the other solutions worked for us.

Solution 4

Slightly off topic, but for others who come here, I resolved this issue in the back end by the following (for spring-boot)

     /**
     * CORS config - used by cors() in configure() DO NOT CHANGE the METDHO NAME
     * 
     * @return
     */
    @Bean()
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Lists.newArrayList("http://localhost:4200"));
        configuration.setAllowedMethods(Lists.newArrayList("GET", "POST", "OPTIONS"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Lists.newArrayList("x-xsrf-token", "XSRF-TOKEN"));
        configuration.setMaxAge(10l);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

Solution 5

You should put on the service on the frontend this { withCredentials: true }

Ex:

this.http.put<class>(url, body, { withCredentials: true });
Share:
44,487
Paolo Stefan
Author by

Paolo Stefan

I love coding, be it for fun or for work. Last year I developed using: Python (Django), PHP (Symfony), Typescript (Angular 5, the Ionic framework), Processing. Now learning: GLSL Looking forward to use again: C, Java, ASM :-)

Updated on January 11, 2022

Comments

  • Paolo Stefan
    Paolo Stefan over 2 years

    I've read the docs and all the related questions on SO, but still Angular's XSRF mechanism isn't working for me: in no way I can make a POST request with the X-XSRF-TOKEN header appended automatically.

    I have an Angular 6 app with a login form.

    It's part of a Symfony (PHP 7.1) website, and the Angular app page, when served from Symfony, sends the correct Cookie (XSRF-TOKEN):

    enter image description here

    My app.module.ts includes the right modules:

    // other imports...
    import {HttpClientModule, HttpClientXsrfModule} from "@angular/common/http";
    
    // ...
    @NgModule({
      declarations: [
        // ...
      ],
      imports: [
        NgbModule.forRoot(),
        BrowserModule,
        // ...
        HttpClientModule,
        HttpClientXsrfModule.withOptions({
          cookieName: 'XSRF-TOKEN',
          headerName: 'X-CSRF-TOKEN'
        }),
        // other imports
      ],
      providers: [],
      entryComponents: [WarningDialog],
      bootstrap: [AppComponent]
    })
    export class AppModule {
    }
    

    Then, inside a Service's method, I'm making the following http request (this.http is an instance of HttpClient):

    this.http
        .post<any>('api/login', {'_username': username, '_pass': password})
        .subscribe(/* handler here */);
    

    The post request never sends the X-XSRF-TOKEN header. Why?

  • Merv
    Merv over 5 years
    In addition to the above: - NO header is set for GET or HEAD requests - Name of cookie must be: XSRF-TOKEN (unless default name is overriden) - Most important make sure your not using absolute paths. With that it is meant paths that start with HTTP or HTTPS. It MUST be a RELATIVE path.
  • Anthony
    Anthony over 5 years
    Does this mean that all of my server side requests must come from my sites domain instead of a dedicated api url? example: localhost:4000/home is the SPA and my apis would be at localhost:4000/api/getSomething instead of localhost:8080/api/getSomething
  • Paolo Stefan
    Paolo Stefan over 5 years
    @Anthony the cookie gets set from a server-side page, and gets read from a SPA, so, both the SPA and the api must be at least on the same 2nd level domain, so that the cookie can be read from the SPA. It could work if the server and the SPA run on different ports of the same domain, like in your example, but I'v not tested this scenario.
  • Evan Sevy
    Evan Sevy over 4 years
    @Merv So, why is GET not protected from XSRF? I'm new to understanding XSRF and may not understand it fully yet, but it seems that a malicious user could 'GET' data they shouldn't be able to. Could you clarify this for me?
  • Arnaud Denoyelle
    Arnaud Denoyelle over 4 years
    @RuneStar That is true but by design, for this reason, you should never expose mutating requests on GET method. GET is particular because it is very easy to trick the user into executing a GET request by displaying an "image" which url is something nasty like <img src="/backend/drop-database">
  • Evan Sevy
    Evan Sevy over 4 years
    @ArnaudDenoyelle Thanks. I understand that you don't want to mutate within a GET because its not meant for that and, as you implied, could introduce a vulnerability if it did. But, I'm wondering if a malicious user can acquire the data that is returned back by a GET? Would it be returned to the original user, or would it be returned within the malicious users 'Cross Site Request'?
  • Merv
    Merv over 4 years
    @RuneStar XSRF protection is not for protecting the web application from unauthorized access, meaning malicious users that gets data by a GET requests. XSRF protection is to protect your official users of your web application from malicious users who can modify/change data on your web application on behalf of your official users. And like Arnaud mentioned. A get request should never be used to modify data. Only to get data. If you want to protect ur app from getting data from malicious users u should look into securing ur URLs from the public with different roles privileges. PM for more info.
  • Evan Sevy
    Evan Sevy over 4 years
    @Merv Say a malicious user somehow gets a victim to access their malicious website after the user has signed into my app. This malicious site has something like the following: <form id="csrf-form" method='GET' action='https:// myApp / api / getSensitiveData'>. Is it possible for the data returned to be sent to the malicious user? To note, I am checking for authentication/authorization.
  • Merv
    Merv over 4 years
    @RuneStar No its not possible (with only CSRF.) Because everything happens on the user's machine. So even if the user is deceived the response (with sensitive data) reaches the user's machine so the malicious user gains nothing from that. The only way the malicious user gains something is if he does(actions/modify/edit) something on the user's behalf that benefits him/her. Like maybe using your online banking to send money to his account. And the user will see the message (if the bank shows a message that money was sent) ....$ was sent to 'malicious user account'. I hope you understood!
  • Jawadh Salih Rifath
    Jawadh Salih Rifath over 4 years
    @PaoloStefan, What is meant by Path = / . And how to set it if we have a Spring boot backend.
  • Jason Powell
    Jason Powell over 4 years
    So far, this is the only solution that has worked for me. But I don't understand why.
  • Gloire
    Gloire about 4 years
    Thanks for the reference to the Angular code. Makes sense now.
  • OzzyTheGiant
    OzzyTheGiant about 3 years
    Yeah this works. Can't be http://... because it doesn't set the header and can't be set without double slashes, at least not during ng serve because I get CORS error (my environments.ts file has a base API url of //example.test + port number, so that http requests can be tested on ng serve and after a build on my local machine)
  • yktoo
    yktoo almost 3 years
    Because of this