From ba13599fcf8a807bf7d6620840013d0299b1ae7c Mon Sep 17 00:00:00 2001 From: Mads Apollo Lauridsen Date: Mon, 23 Sep 2024 17:55:37 +0200 Subject: [PATCH] cleanup of interceptor. Moved methods into constants. A bug exists, where first call will fail, thogh a switchmap has been created. --- src/app/shared/constants/csrf-constants.ts | 3 +- src/app/shared/helpers/csrf-interceptor.ts | 43 +++++++++++----------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/app/shared/constants/csrf-constants.ts b/src/app/shared/constants/csrf-constants.ts index f9667de1..a3d8dc8b 100644 --- a/src/app/shared/constants/csrf-constants.ts +++ b/src/app/shared/constants/csrf-constants.ts @@ -1,2 +1,3 @@ -export const CsrfCookieName = "token-cookie-name"; +export const CsrfCookieName = "x-csrf-cookie"; export const CsrfHeaderName = "x-csrf-token"; +export const IgnoredCsrfMethods = ["GET", "HEAD", "OPTIONS"]; diff --git a/src/app/shared/helpers/csrf-interceptor.ts b/src/app/shared/helpers/csrf-interceptor.ts index cd966f9e..f33241e2 100644 --- a/src/app/shared/helpers/csrf-interceptor.ts +++ b/src/app/shared/helpers/csrf-interceptor.ts @@ -1,9 +1,9 @@ import { HttpClient, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http"; import { Injectable } from "@angular/core"; import { environment } from "@environments/environment"; -import { CsrfHeaderName } from "@shared/constants/csrf-constants"; -import { Observable, of } from "rxjs"; -import { catchError, switchMap, tap } from "rxjs/operators"; +import { CsrfHeaderName, IgnoredCsrfMethods } from "@shared/constants/csrf-constants"; +import { Observable } from "rxjs"; +import { catchError, switchMap } from "rxjs/operators"; @Injectable() export class CsrfInterceptor implements HttpInterceptor { @@ -13,33 +13,34 @@ export class CsrfInterceptor implements HttpInterceptor { constructor(private httpClient: HttpClient) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { - if (this.csrfToken || req.url.endsWith(this.tokenUrl)) { - const cloned = req.clone({ - headers: req.headers.set(CsrfHeaderName, this?.csrfToken ?? ""), - withCredentials: req.withCredentials || ["POST", "PUT", "DELETE"].includes(req.method), + const ignoreMethod = IgnoredCsrfMethods.includes(req.method); + + let requestWithCredentials = req.clone({ + withCredentials: req.withCredentials || !ignoreMethod, + }); + + if (ignoreMethod) { + return next.handle(requestWithCredentials); + } + + if (this.csrfToken) { + const reqWithCsrfToken = requestWithCredentials.clone({ + headers: requestWithCredentials.headers.set(CsrfHeaderName, this.csrfToken), }); - return next.handle(cloned); + return next.handle(reqWithCsrfToken); } // If no CSRF token, fetch it first - return this.fetchCsrfToken().pipe( - switchMap(() => { - return next.handle(req); + return this.httpClient.get<{ token: string }>(this.baseUrl + this.tokenUrl, { withCredentials: true }).pipe( + switchMap(response => { + this.csrfToken = response.token; + return next.handle(requestWithCredentials); }), catchError(err => { console.error("CSRF token fetch failed", err); - return next.handle(req); + return next.handle(requestWithCredentials); }) ); } - - private fetchCsrfToken(): Observable { - return this.httpClient.get<{ token: string }>(this.baseUrl + this.tokenUrl, { withCredentials: true }).pipe( - tap(response => { - this.csrfToken = response.token; - }), - switchMap(response => of(response.token)) - ); - } }