Skip to content

Commit

Permalink
cleanup of interceptor. Moved methods into constants. A bug exists, w…
Browse files Browse the repository at this point in the history
…here first call will fail, thogh a switchmap has been created.
  • Loading branch information
MadsApollo committed Sep 23, 2024
1 parent 1323682 commit ba13599
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/app/shared/constants/csrf-constants.ts
Original file line number Diff line number Diff line change
@@ -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"];
43 changes: 22 additions & 21 deletions src/app/shared/helpers/csrf-interceptor.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -13,33 +13,34 @@ export class CsrfInterceptor implements HttpInterceptor {
constructor(private httpClient: HttpClient) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
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<string> {
return this.httpClient.get<{ token: string }>(this.baseUrl + this.tokenUrl, { withCredentials: true }).pipe(
tap(response => {
this.csrfToken = response.token;
}),
switchMap(response => of(response.token))
);
}
}

0 comments on commit ba13599

Please sign in to comment.