Search code examples
angularhttpcsrfinterceptorpreflight

Angular HTTP interceptor custom preflight


I'm writing an application that communicates with a backend API that requires a XSRF-TOKEN header be set on every request. I've achieved that with the following HTTP interceptor:

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
  const modifiedReq = request.clone({
    headers: request.headers.set('X-XSRF-TOKEN', this.cookieService.get('XSRF-TOKEN')),
  });

  return next.handle(modifiedReq);
}

Now I need to handle the case where the XSRF-TOKEN cookie is not yet set. In this case I will need to preflight the request that I'm sending with one that hits a specific /csrf endpoint on the backend API, which will return a Set-Cookie header to set XSRF-TOKEN.

As a quick and dirty test (without refactoring to remove duplicate code) I tried this:

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
  if (!this.cookieService.check('XSRF-TOKEN')) {
    this.apiService.csrf().subscribe(() => {
      const modifiedReq = request.clone({
        headers: request.headers.set('X-XSRF-TOKEN', this.cookieService.get('XSRF-TOKEN')),
      });

      return next.handle(modifiedReq);
    });
  }

  const modifiedReq = request.clone({
    headers: request.headers.set('X-XSRF-TOKEN', this.cookieService.get('XSRF-TOKEN')),
  });

  return next.handle(modifiedReq);
}

But this obviously has the consequence of an infinite loop of requests to the /csrf endpoint, as each HTTP request inside the interceptor is intercepted itself...

How can I achieve what I'm going for here?


Solution

  • Just let the request to /csrf complete without intercepting it:

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    
      if (request.url === '/csrf') {
        return next.handle(request);
      }
    
      if (!this.cookieService.check('XSRF-TOKEN')) {
        this.apiService.csrf().subscribe(() => {
          const modifiedReq = request.clone({
            headers: request.headers.set('X-XSRF-TOKEN', this.cookieService.get('XSRF-TOKEN')),
          });
    
          return next.handle(modifiedReq);
        });
      }
    
      const modifiedReq = request.clone({
        headers: request.headers.set('X-XSRF-TOKEN', this.cookieService.get('XSRF-TOKEN')),
      });
    
      return next.handle(modifiedReq);
    }