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
This commit is contained in:
Willem Jiang
2026-05-13 15:46:29 +08:00
parent e9deb6c2f2
commit 4dc328e460
5 changed files with 42 additions and 30 deletions
+4 -3
View File
@@ -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 });
+18 -14
View File
@@ -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();
+2 -1
View File
@@ -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
</Link>
<Link
href="/api/v1/auth/logout"
href={`${getBackendBaseURL()}/api/v1/auth/logout`}
className="text-muted-foreground hover:bg-muted rounded-md border px-4 py-2 text-sm"
>
Logout &amp; Reset
@@ -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();
+4 -2
View File
@@ -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",
});