In angular documentation, it is mentioned that the angular httpclient
will automatically send the value of cookie XSRF-TOKEN
in the header X-XSRF-TOKEN
of post request. Documentation link
But it does not send the header for me. Here is my code
Nodejs code to set the cookie
router.get('/set-csrf',function(req,res,next){
res.setHeader('Set-Cookie', "XSRF-TOKEN=abc;Path=/; HttpOnly; SameSite=Strict");
res.send();
})
I have used the httpclient in app.module.ts
imports: [
HttpClientModule
]
** The above code is just for debug purpose. I do not have a set-csrf endpoint.
But it does not send any header when I send a post request. I am not able to debug.
I have added the issue in the github repository of angular too. HttpXsrfInterceptor checks if the request is GET or HEAD, or if it starts with http. If true, it skips adding the header.
Here is the code in HttpXsrfInterceptor class
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const lcUrl = req.url.toLowerCase();
// Skip both non-mutating requests and absolute URLs.
// Non-mutating requests don't require a token, and absolute URLs require special handling
// anyway as the cookie set
// on our origin is not the same as the token expected by another origin.
if (req.method === 'GET' || req.method === 'HEAD' || lcUrl.startsWith('http://') ||
lcUrl.startsWith('https://')) {
return next.handle(req);
}
const token = this.tokenService.getToken();
// Be careful not to overwrite an existing header of the same name.
if (token !== null && !req.headers.has(this.headerName)) {
req = req.clone({headers: req.headers.set(this.headerName, token)});
}
return next.handle(req);
}
I am not sure why they have skipped for http/s part. Here is my issue in github
What you are looking for is HttpClientXsrfModule
.
Please read more about it here: https://angular.io/api/common/http/HttpClientXsrfModule.
Your usage should be like this:
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie', // this is optional
headerName: 'My-Xsrf-Header' // this is optional
})
]
Additionally, if your code targets API via absolute URL, default CSRF interceptor will not work out of the box. Instead you have to implement your own interceptor which does not ignore absolute routes.
@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const headerName = 'X-XSRF-TOKEN';
let token = this.tokenExtractor.getToken() as string;
if (token !== null && !req.headers.has(headerName)) {
req = req.clone({ headers: req.headers.set(headerName, token) });
}
return next.handle(req);
}
}
And finally add it to your providers:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true }
]