diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 479a577..4c5fde9 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -15,6 +15,23 @@ export const apiClient = axios.create({ }, }); +// CSRF token storage — persisted in sessionStorage, sent on all unsafe methods +const CSRF_KEY = 'leocrm_csrf_token'; +let csrfToken: string | null = sessionStorage.getItem(CSRF_KEY); + +export function setCsrfToken(token: string | null) { + csrfToken = token; + if (token) { + sessionStorage.setItem(CSRF_KEY, token); + } else { + sessionStorage.removeItem(CSRF_KEY); + } +} + +export function getCsrfToken(): string | null { + return csrfToken; +} + let onUnauthorized: (() => void) | null = null; let onValidationError: ((errors: Record) => void) | null = null; @@ -28,6 +45,11 @@ export function setValidationErrorHandler(handler: (errors: Record { + // Attach CSRF token on unsafe methods + const unsafe = ['post', 'put', 'patch', 'delete']; + if (unsafe.includes(config.method?.toLowerCase() ?? '') && csrfToken) { + config.headers['X-CSRF-Token'] = csrfToken; + } return config; }, (error) => Promise.reject(error) diff --git a/frontend/src/api/hooks.ts b/frontend/src/api/hooks.ts index 94f8191..d413515 100644 --- a/frontend/src/api/hooks.ts +++ b/frontend/src/api/hooks.ts @@ -1,5 +1,5 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { apiPost, apiGet, apiPatch, apiDelete, apiClient } from './client'; +import { apiPost, apiGet, apiPatch, apiDelete, apiClient, setCsrfToken } from './client'; import { useAuthStore } from '@/store/authStore'; export interface LoginPayload { @@ -112,6 +112,10 @@ export function useLogin() { onSuccess: (data: any) => { setUser(data.user || data); setError(null); + // Store CSRF token for subsequent unsafe requests + if (data.csrf_token) { + setCsrfToken(data.csrf_token); + } }, onError: (error: any) => { setError(error.message || 'Login failed'); @@ -126,6 +130,7 @@ export function useLogout() { mutationFn: () => apiPost('/auth/logout'), onSettled: () => { logout(); + setCsrfToken(null); queryClient.clear(); }, }); diff --git a/frontend/src/api/mail.ts b/frontend/src/api/mail.ts index b995743..164819c 100644 --- a/frontend/src/api/mail.ts +++ b/frontend/src/api/mail.ts @@ -401,7 +401,7 @@ export function deleteFolder(folderId: string): Promise { export function fetchMails(folderId: string, page: number): Promise { const qs = new URLSearchParams({ folder_id: folderId, page: String(page) }).toString(); - return apiGet(`/mail/?${qs}`); + return apiGet(`/mail?${qs}`); } export function getMail(mailId: string): Promise {