From 4dc328e460a9af852913bda578a504b6755f2689 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Wed, 13 May 2026 15:46:29 +0800 Subject: [PATCH] fix(auth): use getBackendBaseURL() in auth-related fetch calls Auth pages (login, setup) and components (AuthProvider, account-settings, workspace layout) used hardcoded relative paths like /api/v1/auth/... instead of the configurable getBackendBaseURL() used by the rest of the codebase. This prevented them from reaching the backend when NEXT_PUBLIC_BACKEND_BASE_URL was set to a different origin. Closes #2859 --- frontend/src/app/(auth)/login/page.tsx | 7 ++-- frontend/src/app/(auth)/setup/page.tsx | 32 +++++++++++-------- frontend/src/app/workspace/layout.tsx | 3 +- .../settings/account-settings-page.tsx | 24 ++++++++------ frontend/src/core/auth/AuthProvider.tsx | 6 ++-- 5 files changed, 42 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/(auth)/login/page.tsx b/frontend/src/app/(auth)/login/page.tsx index 82fcf8b90..d79563d1a 100644 --- a/frontend/src/app/(auth)/login/page.tsx +++ b/frontend/src/app/(auth)/login/page.tsx @@ -10,6 +10,7 @@ import { FlickeringGrid } from "@/components/ui/flickering-grid"; import { Input } from "@/components/ui/input"; import { useAuth } from "@/core/auth/AuthProvider"; import { parseAuthError } from "@/core/auth/types"; +import { getBackendBaseURL } from "@/core/config"; /** * Validate next parameter @@ -71,7 +72,7 @@ export default function LoginPage() { useEffect(() => { let cancelled = false; - void fetch("/api/v1/auth/setup-status") + void fetch(`${getBackendBaseURL()}/api/v1/auth/setup-status`) .then((r) => r.json()) .then((data: { needs_setup?: boolean }) => { if (!cancelled && data.needs_setup) { @@ -94,8 +95,8 @@ export default function LoginPage() { try { const endpoint = isLogin - ? "/api/v1/auth/login/local" - : "/api/v1/auth/register"; + ? `${getBackendBaseURL()}/api/v1/auth/login/local` + : `${getBackendBaseURL()}/api/v1/auth/register`; const body = isLogin ? `username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}` : JSON.stringify({ email, password }); diff --git a/frontend/src/app/(auth)/setup/page.tsx b/frontend/src/app/(auth)/setup/page.tsx index 4f1d21eae..f9af1ea77 100644 --- a/frontend/src/app/(auth)/setup/page.tsx +++ b/frontend/src/app/(auth)/setup/page.tsx @@ -10,6 +10,7 @@ import { Input } from "@/components/ui/input"; import { getCsrfHeaders } from "@/core/api/fetcher"; import { useAuth } from "@/core/auth/AuthProvider"; import { parseAuthError } from "@/core/auth/types"; +import { getBackendBaseURL } from "@/core/config"; type SetupMode = "loading" | "init_admin" | "change_password"; @@ -36,7 +37,7 @@ export default function SetupPage() { setMode("change_password"); } else if (!isAuthenticated) { // Check if the system has no users yet - void fetch("/api/v1/auth/setup-status") + void fetch(`${getBackendBaseURL()}/api/v1/auth/setup-status`) .then((r) => r.json()) .then((data: { needs_setup?: boolean }) => { if (cancelled) return; @@ -72,7 +73,7 @@ export default function SetupPage() { setLoading(true); try { - const res = await fetch("/api/v1/auth/initialize", { + const res = await fetch(`${getBackendBaseURL()}/api/v1/auth/initialize`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", @@ -113,19 +114,22 @@ export default function SetupPage() { setLoading(true); try { - const res = await fetch("/api/v1/auth/change-password", { - method: "POST", - headers: { - "Content-Type": "application/json", - ...getCsrfHeaders(), + const res = await fetch( + `${getBackendBaseURL()}/api/v1/auth/change-password`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + ...getCsrfHeaders(), + }, + credentials: "include", + body: JSON.stringify({ + current_password: currentPassword, + new_password: newPassword, + new_email: email || undefined, + }), }, - credentials: "include", - body: JSON.stringify({ - current_password: currentPassword, - new_password: newPassword, - new_email: email || undefined, - }), - }); + ); if (!res.ok) { const data = await res.json(); diff --git a/frontend/src/app/workspace/layout.tsx b/frontend/src/app/workspace/layout.tsx index c2d567339..fe2378491 100644 --- a/frontend/src/app/workspace/layout.tsx +++ b/frontend/src/app/workspace/layout.tsx @@ -4,6 +4,7 @@ import { redirect } from "next/navigation"; import { AuthProvider } from "@/core/auth/AuthProvider"; import { getServerSideUser } from "@/core/auth/server"; import { assertNever } from "@/core/auth/types"; +import { getBackendBaseURL } from "@/core/config"; import { WorkspaceContent } from "./workspace-content"; @@ -44,7 +45,7 @@ export default async function WorkspaceLayout({ Retry Logout & Reset diff --git a/frontend/src/components/workspace/settings/account-settings-page.tsx b/frontend/src/components/workspace/settings/account-settings-page.tsx index 6c1577162..a88a76590 100644 --- a/frontend/src/components/workspace/settings/account-settings-page.tsx +++ b/frontend/src/components/workspace/settings/account-settings-page.tsx @@ -8,6 +8,7 @@ import { Input } from "@/components/ui/input"; import { fetch, getCsrfHeaders } from "@/core/api/fetcher"; import { useAuth } from "@/core/auth/AuthProvider"; import { parseAuthError } from "@/core/auth/types"; +import { getBackendBaseURL } from "@/core/config"; import { useI18n } from "@/core/i18n/hooks"; import { SettingsSection } from "./settings-section"; @@ -38,17 +39,20 @@ export function AccountSettingsPage() { setLoading(true); try { - const res = await fetch("/api/v1/auth/change-password", { - method: "POST", - headers: { - "Content-Type": "application/json", - ...getCsrfHeaders(), + const res = await fetch( + `${getBackendBaseURL()}/api/v1/auth/change-password`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + ...getCsrfHeaders(), + }, + body: JSON.stringify({ + current_password: currentPassword, + new_password: newPassword, + }), }, - body: JSON.stringify({ - current_password: currentPassword, - new_password: newPassword, - }), - }); + ); if (!res.ok) { const data = await res.json(); diff --git a/frontend/src/core/auth/AuthProvider.tsx b/frontend/src/core/auth/AuthProvider.tsx index 652cc49b8..81062dfbc 100644 --- a/frontend/src/core/auth/AuthProvider.tsx +++ b/frontend/src/core/auth/AuthProvider.tsx @@ -10,6 +10,8 @@ import React, { type ReactNode, } from "react"; +import { getBackendBaseURL } from "@/core/config"; + import { type User, buildLoginUrl } from "./types"; // Re-export for consumers @@ -56,7 +58,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) { const refreshUser = useCallback(async () => { try { setIsLoading(true); - const res = await fetch("/api/v1/auth/me", { + const res = await fetch(`${getBackendBaseURL()}/api/v1/auth/me`, { credentials: "include", }); @@ -88,7 +90,7 @@ export function AuthProvider({ children, initialUser }: AuthProviderProps) { setUser(null); try { - await fetch("/api/v1/auth/logout", { + await fetch(`${getBackendBaseURL()}/api/v1/auth/logout`, { method: "POST", credentials: "include", });