angular: HttpClient does not set X-XSRF-Token on Http Post

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ X] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Updated to angular 5.0.2. Updated from the deprecated HttpModule (@angular/http) to HttpClientModule (@angular/common/http).

Updated Http.post to HttpClient.post and tested. X-XSRF-TOKEN was not present in the Http Header.

HttpClient (@angular/common/http) does not set X-XSRF-Token on Http Post, while Http (@angular/http) does.

Expected behavior

HttpClient should set the X-XSRF-TOKEN on Http Post.

Minimal reproduction of the problem with instructions

Verify that javascript XSRF-TOKEN cookie has been set.

Test HttpClient (@angular/http) and HttpClientModule (@angular/common/http) side by side with nearly identical Http post requests.


//OLD
        CreateOld(sample: Models.Sample): Observable<Models.Sample[]> {
            let body = JSON.stringify(sample);
            let headers = new Headers({ 'Content-Type': 'application/json' });
            return this.http
                .post(this.baseUrl + 'api/sample/Create', body, { headers: headers })
                .map(response => { return response.json() as Models.Task[] });
        }
//NEW
	 CreateNew(sample: Models.Sample): Observable<Models.Sample[]> {
			let body = JSON.stringify(sample)
			return this.httpClient
				.post<Models.Task[]>(this.baseUrl + 'api/sample/Create', body, { headers: new HttpHeaders().set('Content-Type', 'application/json') });
        }

What is the motivation / use case for changing the behavior?

Environment


Angular version: 5.0.2


Browser:
- [ X] Chrome (desktop) version 62.0.3202.94 
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [X ] Edge version 41.16299.15.0
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 36
  • Comments: 54 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Hello, we reviewed this issue and determined that it doesn’t fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag angular.

If you are wondering why we don’t resolve support issues via the issue tracker, please check out this explanation.


You’re probably after https://angular.io/api/common/http/HttpClientXsrfModule

@bhanukumar04 If you look at Angular’s xsrf implementation here You can see in the interceptor they have this

if (req.method === 'GET' || req.method === 'HEAD' || lcUrl.startsWith('http://') ||
        lcUrl.startsWith('https://')) {
      return next.handle(req);
    }

If any of the condition are true then adding the header is skipped. That’s why I had to make my own since my API is on a different domain.

I am also facing the same issue with angular 4. I can see the cookie in the response headers but while posting data angular is not adding appropriate headers , also I have tried with HttpClientXsrfModule.

Same problem Angular 5.2.5 with Spring boot 1.5.4. I’ve tried all solutions including writing my own interceptor, but am getting null on let token = this.tokenExtractor.getToken() as string;

@bhanukumar04 I just created an Interceptor and did it myself.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        let requestToForward = req;
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null) {
            requestToForward = req.clone({ setHeaders: { "X-XSRF-TOKEN": token } });
        }
        return next.handle(requestToForward);
    }

Yes, we already have that implemented. Still an issue.

Per your documentation on HttpClientModule, please see the section in bold. This is either a bug or a invalid documentation.

"Security: XSRF Protection

Cross-Site Request Forgery (XSRF) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. HttpClient supports a common mechanism used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default XSRF-TOKEN, and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.

By default, an interceptor sends this cookie on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.

To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on either"

I have found another bug report #18859, “HttpClient HttpXsrfInterceptor does not set xsrf token for absolute urls” which is a duplicate of this issue.

Any update? I have my XSRF-TOKEN non-HttpOnly and angular still does not set X-XSRF-TOKEN

@merv0190 Geez, why are you shouting?

Haha. Im not, just putting emphasis. 😉

Then the italic is a better choice 😃

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.

IIRC:

  • HttpClientXsrfModule does not add the header on requests using absolute urls.
  • HttpClientXsrfModule does not add the header unless the path is set to /.
  • HttpClientXsrfModule does not add the header unless the cookie is set to httpOnly=false.
  • Points 1 & 3 make sense. However, none of the above is in the documentation.

Why has this information not been in the documentation for almost five years?

am also facing the same issue with Angular 5.2.X version, is there any work around for it. In the first request am getting XSRF-TOKEN cookie in response and in the next requests am expecting to add cookie by angular which is not happening and my XSRF-TOKEN non-HttpOnly.

On Stack Overflow there’s a nice comment about this issue which also states that some part of the Angular documentation isn’t precise in terms of X-XSRF-Token, link https://stackoverflow.com/a/50511663/11127383

@JMoney417

  1. Httponly false because if true, angular or any other JavaScript won’t be able to access it.
  2. CSRF not designed for GET or HEAD requests.
  3. XSRF-TOKEN name because angular expects this name. This is the default if you want another name google how to change the angular default and you will find.
  4. Angular default again it expects the cookie to be at the root of the domain. (Guessing)
  5. And no absolute paths because cookies are only sent for the same domain. Cookies are domain binded.

Hope it helps. Google point 4 😃

@merv0190 Geez, why are you shouting?

Does anyone can help me in modifying the below code written

As I’m Unable to generate cookies and add the XSRF Token So the XSRF Token must be printed in the backend of my PHP script As I’ve Seen from the @bhanukumar04 and rest of the users getting successfull

Here is my below code in an Angular 6

import {HttpClient, HttpClientModule, HttpClientXsrfModule, HttpHeaders, HttpParams} from "@angular/common/http";

constructor(private _http:HttpClient) { }
...
apiURL = "//localhost/simple_api/insert.php";

addUser(info){
    return this._http.post(this.apiURL, info, {
      headers:  { 'Content-Type': 'application/x-www-form-urlencoded' },
      withCredentials: true,
      responseType: "arraybuffer",
    }
    )
    .subscribe(
                data => {
                    console.log("POST Request is successful ", data);
                },
                error => {
                    console.log("Error", error);
                }
            ); 
  }

Since I’ve changed the apiURL = “http://localhost/simple_api/insert.php”; TO apiURL = “//localhost/simple_api/insert.php”;

Working Fine. But unable var_dump or print the the headers attached in the PHP script. But I’m getting the form data submitted successfully.

Had exact same issue. Frontend runs on path "/". Backend runs on path "/myApp". Backend was setting cookie path as "/myApp". Changing it to "/" fixed my problem.

Actually, I consider it a bug that //example.com works - that’s just an absolute path that we don’t detect, the only difference is that the protocol is implied.

In general I agree our documentation could be a lot better here. Marking this as a confusing behavior.

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.

That is what is meant with absolute path. It does not work with absolute path. The change you made was to a relative path and that is why it is now working. ‘https://example.com’ < absolute path ‘//example.com’ relative path

@Loque18 Your issue: “Documenting things to keep devs from wasting their time” was reported and immediately closed as “not a priority” during the Angular 2 RC process.

This is what I found as of a few years ago: https://github.com/angular/angular/issues/20511#issuecomment-955607609

Any Angular codebase is going to be filled with notes about undocumented behaviors and workarounds for obvious defects that reference years old tickets.

it almost made m[e] cry

Welcome to the club, we had jackets made… So many tear stained jackets.

My Two Cents:

To me this is not only a confusing behavior but a missing feature, I’d say a very important one! Not only this doesn’t work in development mode when we use different ports, but it doesn’t work even when the absolute URL points to the same first level domain:

  • myapp.com
  • api.myapp.com

I can see 2 possible solutions:

  • We remove this check entirely (would it really be dangerous if we sent a XSRF-Token to another domain? What could it do with it? What would the security implications be? Just thinking out loud 😃)
  • We look at the first-level domain and/or we let the developer specify a set of trusted domains

@merv0190 what if there is an absolute path ? Is there a way how to deal with that ?

To my knowledge there isn’t because, absolute path means that the URL your trying to make a request to is not on the same domain. And cookies are domain binded. I’m guessing your on ‘http://www.firstweb.com’ and want to make a request to ‘http://www.secondweb.com/getBooks’. If not then you could just do relativepath ‘/getbooks’ instead of ‘http://www.firstweb.com/getBooks

Hope it helps!

There are a few conditions that must be satisfied for this to work.

  1. HTTPONLY SET TO FALSE
  2. NOT GET, HEAD REQUESTS
  3. NAME OF COOKIE MUST BE SET FROM THE SERVER AND HAVE THE NAME XSRF-TOKEN
  4. PATH OF COOKIE MUST BE “/”
  5. MOST IMPORTANT MAKE SURE YOUR NOT USING ABSOLUTE PATHS. WITH THAT I MEAN PATHS THAT STARTS WITH HTTP:// OR HTTPS://. IT MUST BE A RELATIVE PATH

Angular: 5.2.9 still same problem