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 {
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 {
let body = JSON.stringify(sample)
return this.httpClient
.post(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:
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
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.
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.
Any update? I have my XSRF-TOKEN non-HttpOnly and angular still does not set X-XSRF-TOKEN
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.
@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);
}
From back end we need to send response with Path=/; HttpOnly=false on first get or login. res.append('Set-Cookie', 'tracking_cookie'=cookie_name; Path=/; HttpOnly=false');
Then writing custom interceptor from Angular to set X-XSRF-TOKEN on each request worked for me.
@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.
Even when added both HttpClientXsrfModule & HttpClientModule the X-XSRF-TOKEN, still not working. Previously i am on the HttpModule and its working.
@irowbin make you have your XSRF cookie coming from back end should be HttpOnly=false, then only angular will be able to ready the cookie and send in next requests
@bhanukumar04 Yep. i did that already and I am getting that error on get method;
@bhanukumar04 I have done as you said, about custom angular interceptor to set header. I have issue with first post request. If I send post request first time I get error that my CSRF token is not in cookie. For second time it works good. What can I do with that?
@tararas124 before your make actual post request , make one get request to get the CSRF cookie, then make actual POST request and this time cookie will be available from the first get request.
@bhanukumar04 should I do this inside interceptor? If token is not in cookie, then get request for taking it. Do I understand it right?
@tararas124 if your are making login request as first request , with successful authentication send the cookie and from the next request it will be available. If you are not making login request make one get request just to get the cookie and then make your post request, this is not necessarily to be done in interceptor.
@bhanukumar04 I have done as you said, but It doesn't work. When I send get request before post. They are asynchronous and my post sends faster then get ends. You know what I mean? How to make this calls synchronous? Or some work around Ty so much in advance !
@tararas124 subscribe to the get call ,then on complete of get call notification, call your post call and that should work http.get(url) .subscribe(() => {}, () => {}, () => { // call you post call here });
Angular: 5.2.9 still same problem
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;
This is working for me. Note that you have to send the cookie from the server with HttpOnly = false or Angular won't be able to read the cookie.
Double checked the HttpOnly flag is false through Postman. Something else is blocking the access, not sure what
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.
@Klinton90 , when i set path as "/" and HttpOnly flag as false the only angular is able to read the cookie. can you please provide the sample how you are reading cookie from Angular side? Thank you.
I also have this issue, as Angular interceptor ONLY sets X-CSRF-Token header when the request URL is absolute, not relative (as @ewaschenko mentioned).
(https://github.com/angular/angular/blob/2a236b40666c1714cee35efc42bb059761a24a40/packages/common/http/src/xsrf.ts#L81)
Does anybody know why there is such condition?
Hi !
I am having the same problem.
My backend and frontend don't run on the same domain then, because of those conditions, the X-XSRF-TOKEN header is not set.
I don't know much about CSRF attacks but I don't see why you would use CSRF protection for cases where backend run on a different domain.
Would you consider removing those conditions ? Or at least allow to choose not to apply them based on some configuration ?
@JulieRossi No, they don't consider removing these conditions. Here is a comment from the other issue about that: https://github.com/angular/angular/issues/18859#issuecomment-339463342.
I think the biggest problem is incomplete documentation. It should emphasize this default behaviour of the interceptor. It would remove most of the confusion. After all, writing your own interceptor is easy, so if you need to have this header cross-origin, it's not a problem.
Ok, thank you @Olgagr for the answer !
@Olgagr Did you only provide your interceptor or also a token extractor?
Using a custom interceptor that reads from the injected document doesn't help for me. It looks like the token extractor is not able to read the cookie received for the second domain.
the one doesn't work for absolute url's, there's an active issue -> #18859
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.