From eba009d3ceb81de6d2ae03b649e5c7303f54b11c Mon Sep 17 00:00:00 2001 From: fallenbagel <98979876+Fallenbagel@users.noreply.github.com> Date: Thu, 17 Oct 2024 06:21:15 +0800 Subject: [PATCH] fix: fetch override to attach XSRF token to fix csrfProtection issue During the migration from Axios to fetch, we overlooked the fact that Axios automatically handled CSRF tokens, while fetch does not. When CSRF protection was turned on, requests were failing with an "invalid CSRF token" error for users accessing the app even via HTTPS. This commit overrides fetch to ensure that the CSRF token is included in all requests. fix #1011 --- src/pages/_app.tsx | 1 + src/utils/fetchOverride.ts | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/utils/fetchOverride.ts diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ba0677c64..e5704052d 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -12,6 +12,7 @@ import { SettingsProvider } from '@app/context/SettingsContext'; import { UserContext } from '@app/context/UserContext'; import type { User } from '@app/hooks/useUser'; import '@app/styles/globals.css'; +import '@app/utils/fetchOverride'; import { polyfillIntl } from '@app/utils/polyfillIntl'; import { MediaServerType } from '@server/constants/server'; import type { PublicSettingsResponse } from '@server/interfaces/api/settingsInterfaces'; diff --git a/src/utils/fetchOverride.ts b/src/utils/fetchOverride.ts new file mode 100644 index 000000000..e0a900125 --- /dev/null +++ b/src/utils/fetchOverride.ts @@ -0,0 +1,46 @@ +const getCsrfToken = (): string | null => { + if (typeof window !== 'undefined') { + const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/); + return match ? decodeURIComponent(match[1]) : null; + } + return null; +}; + +const isSameOrigin = (url: RequestInfo | URL): boolean => { + const parsedUrl = new URL( + url instanceof Request ? url.url : url.toString(), + window.location.origin + ); + return parsedUrl.origin === window.location.origin; +}; + +// We are using a custom fetch implementation to add the X-XSRF-TOKEN heade +// to all requests. This is required when CSRF protection is enabled. +if (typeof window !== 'undefined') { + const originalFetch: typeof fetch = window.fetch; + + (window as typeof globalThis).fetch = async ( + input: RequestInfo | URL, + init?: RequestInit + ): Promise => { + if (!isSameOrigin(input)) { + return originalFetch(input, init); + } + + const csrfToken = getCsrfToken(); + + const headers = { + ...(init?.headers || {}), + ...(csrfToken ? { 'XSRF-TOKEN': csrfToken } : {}), + }; + + const newInit: RequestInit = { + ...init, + headers, + }; + + return originalFetch(input, newInit); + }; +} + +export {};