All files / app/http csrf.interceptor.ts

33.33% Statements 6/18
25% Branches 6/24
16.66% Functions 1/6
23.07% Lines 3/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45            1x   1x     1x                                                                  
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable, isDevMode } from '@angular/core';
import { catchError, Observable, throwError } from 'rxjs';
import { ConfigService } from '../service/config.service';
 
@Injectable()
export class CsrfInterceptor implements HttpInterceptor {
 
  withCredentials = isDevMode() || this.config.electron || location.hostname === 'localhost';
 
  constructor(
    private config: ConfigService,
  ) {}
 
  private getCsrfToken(): string {
    return document.cookie.split('; ').find(row => row.startsWith('XSRF-TOKEN='))?.split('=')?.[1] || '';
  }
 
  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    if (request.method === 'GET' || request.method === 'HEAD' || request.method === 'OPTIONS') {
      return next.handle(request);
    }
 
    const modifiedReq = request.clone({
      headers: request.headers.set('X-XSRF-TOKEN', this.getCsrfToken()),
      withCredentials: this.withCredentials,
    });
    return next.handle(modifiedReq).pipe(
      catchError(err => {
        if (!err.status || err.status === 403 && err.error?.detail?.startsWith('Invalid CSRF Token')) {
          // Sometimes the first request has an invalid CSRF token and fails
          // Retry one more time
          console.warn('Retrying forbidden request with fresh CSRF token');
          const retryReq = request.clone({
            headers: request.headers.set('X-XSRF-TOKEN', this.getCsrfToken()),
            withCredentials: this.withCredentials,
          });
          return next.handle(retryReq);
        }
        return throwError(() => err);
      }),
    );
  }
}