From d0f5b0c3c91cb1fca012b5af930804eba25c9192 Mon Sep 17 00:00:00 2001 From: Alexander Dreweke Date: Fri, 24 Jul 2026 16:40:31 +0200 Subject: [PATCH] fix(frontend): add credentials: 'include' to auth cookie fetches Ensure auth cookies are sent on cross-origin API requests that were missing credentials: 'include', including auth-info config, file browser, TTS/STT, and the opencode event stream transport. --- frontend/src/api/stt.test.ts | 1 + frontend/src/api/stt.ts | 1 + frontend/src/components/file-browser/FileBrowser.tsx | 8 ++++++-- frontend/src/components/file-browser/FilePreview.tsx | 1 + frontend/src/contexts/AuthContext.tsx | 2 +- frontend/src/contexts/TTSContext.tsx | 1 + frontend/src/lib/auth-loaders.ts | 2 +- .../src/lib/opencode-event-stream/browserTransport.ts | 1 + 8 files changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/src/api/stt.test.ts b/frontend/src/api/stt.test.ts index ca0f7113b..03749d23f 100644 --- a/frontend/src/api/stt.test.ts +++ b/frontend/src/api/stt.test.ts @@ -128,6 +128,7 @@ describe('WAV extension selection logic', () => { expect.objectContaining({ method: 'POST', body: expect.any(FormData), + credentials: 'include', }) ) diff --git a/frontend/src/api/stt.ts b/frontend/src/api/stt.ts index 01d6291d4..9e76768a3 100644 --- a/frontend/src/api/stt.ts +++ b/frontend/src/api/stt.ts @@ -68,6 +68,7 @@ export const sttApi = { try { const response = await fetch(urlObj.toString(), { + credentials: 'include', method: 'POST', body: formData, signal: controller.signal, diff --git a/frontend/src/components/file-browser/FileBrowser.tsx b/frontend/src/components/file-browser/FileBrowser.tsx index c6df1a35d..b5cde8244 100644 --- a/frontend/src/components/file-browser/FileBrowser.tsx +++ b/frontend/src/components/file-browser/FileBrowser.tsx @@ -169,7 +169,7 @@ useEffect(() => { setError(null) try { - const response = await fetch(getFileApiUrl(path)) + const response = await fetch(getFileApiUrl(path), { credentials: 'include' }) if (!response.ok) { throw new Error(`Failed to load files: ${response.statusText}`) } @@ -251,7 +251,7 @@ useEffect(() => { // Fetch the full file content when selecting a file setLoading(true) try { - const response = await fetch(getFileApiUrl(file.path)) + const response = await fetch(getFileApiUrl(file.path), { credentials: 'include' }) if (!response.ok) { throw new Error(`Failed to load file: ${response.statusText}`) } @@ -294,6 +294,7 @@ useEffect(() => { try { const response = await fetch(getFileApiUrl(currentPath), { + credentials: 'include', method: 'POST', body: formData, }) @@ -364,6 +365,7 @@ useEffect(() => { const handleCreateFile = useCallback(async (name: string, type: 'file' | 'folder') => { try { const response = await fetch(getFileApiUrl([currentPath, name].filter(Boolean).join('/')), { + credentials: 'include', method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type, content: type === 'file' ? '' : undefined }), @@ -382,6 +384,7 @@ useEffect(() => { const handleDelete = useCallback(async (path: string) => { try { const response = await fetch(getFileApiUrl(path), { + credentials: 'include', method: 'DELETE', }) @@ -399,6 +402,7 @@ useEffect(() => { const handleRename = useCallback(async (oldPath: string, newPath: string) => { try { const response = await fetch(getFileApiUrl(oldPath), { + credentials: 'include', method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ newPath }), diff --git a/frontend/src/components/file-browser/FilePreview.tsx b/frontend/src/components/file-browser/FilePreview.tsx index 9f8dfcb33..6931a3ade 100644 --- a/frontend/src/components/file-browser/FilePreview.tsx +++ b/frontend/src/components/file-browser/FilePreview.tsx @@ -117,6 +117,7 @@ export const FilePreview = memo(function FilePreview({ file, hideHeader = false, const saveFileContent = useCallback(async (content: string) => { const response = await fetch(getFileApiUrl(file.path), { + credentials: 'include', method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'file', content }), diff --git a/frontend/src/contexts/AuthContext.tsx b/frontend/src/contexts/AuthContext.tsx index ec826ba34..229766077 100644 --- a/frontend/src/contexts/AuthContext.tsx +++ b/frontend/src/contexts/AuthContext.tsx @@ -41,7 +41,7 @@ export function AuthProvider({ children }: AuthProviderProps) { useEffect(() => { const fetchConfig = async () => { try { - const response = await fetch('/api/auth-info/config') + const response = await fetch('/api/auth-info/config', { credentials: 'include' }) if (response.ok) { const data = await response.json() setConfig(data) diff --git a/frontend/src/contexts/TTSContext.tsx b/frontend/src/contexts/TTSContext.tsx index d5d814fd4..abe63edc1 100644 --- a/frontend/src/contexts/TTSContext.tsx +++ b/frontend/src/contexts/TTSContext.tsx @@ -115,6 +115,7 @@ export function TTSProvider({ children }: TTSProviderProps) { try { const response = await fetch(`${API_BASE_URL}/api/tts/synthesize`, { + credentials: 'include', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }), diff --git a/frontend/src/lib/auth-loaders.ts b/frontend/src/lib/auth-loaders.ts index cce1af144..61fb3829b 100644 --- a/frontend/src/lib/auth-loaders.ts +++ b/frontend/src/lib/auth-loaders.ts @@ -15,7 +15,7 @@ async function fetchAuthConfig(): Promise { isFirstUser: false, adminConfigured: false, } - const response = await fetch('/api/auth-info/config') + const response = await fetch('/api/auth-info/config', { credentials: 'include' }) if (!response.ok) { return defaultConfig } diff --git a/frontend/src/lib/opencode-event-stream/browserTransport.ts b/frontend/src/lib/opencode-event-stream/browserTransport.ts index 1051ee65a..5c5731ae0 100644 --- a/frontend/src/lib/opencode-event-stream/browserTransport.ts +++ b/frontend/src/lib/opencode-event-stream/browserTransport.ts @@ -20,6 +20,7 @@ export function createBrowserEventStreamTransport(): EventStreamTransport { async post(path: string, body: unknown): Promise { const response = await fetch(path, { + credentials: 'include', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),