diff --git a/src/app/layout.test.tsx b/src/app/layout.test.tsx
index f295436..9ab9a6a 100644
--- a/src/app/layout.test.tsx
+++ b/src/app/layout.test.tsx
@@ -27,6 +27,12 @@ vi.mock("@/components/theme-toggle", () => ({
},
}));
+vi.mock("@/components/auth-controls", () => ({
+ AuthControls: function AuthControls() {
+ return null;
+ },
+}));
+
vi.mock("next/link", () => ({
default: function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
return React.createElement("a", { href, "data-nav": "true" }, children);
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 193b937..cb31d0a 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -4,6 +4,7 @@ import Link from "next/link";
import "./globals.css";
import { ThemeToggle } from "@/components/theme-toggle";
import { MobileNav } from "@/components/mobile-nav";
+import { AuthControls } from "@/components/auth-controls";
import { getVersionLabel } from "@/lib/version";
const inter = Inter({ subsets: ["latin"] });
@@ -81,7 +82,8 @@ export default function RootLayout({
{getVersionLabel()}
-
diff --git a/src/components/auth-controls.tsx b/src/components/auth-controls.tsx
new file mode 100644
index 0000000..2d09208
--- /dev/null
+++ b/src/components/auth-controls.tsx
@@ -0,0 +1,28 @@
+import { getAuthMode } from "@/lib/auth";
+import { getSession } from "@/lib/session";
+import { LogoutButton } from "./logout-button";
+
+/**
+ * Server component that conditionally renders logout controls.
+ *
+ * Shows the logout button when:
+ * - OIDC mode is active AND a session exists, OR
+ * - Basic Auth mode is active (credentials are managed client-side)
+ */
+export async function AuthControls() {
+ const authMode = getAuthMode();
+
+ if (!authMode || authMode === "disabled") {
+ return null;
+ }
+
+ // In OIDC mode, only show logout if there's an active session
+ if (authMode === "oidc") {
+ const session = await getSession();
+ if (!session?.user) {
+ return null;
+ }
+ }
+
+ return ;
+}
diff --git a/src/components/logout-button.tsx b/src/components/logout-button.tsx
index aa67032..34af26e 100644
--- a/src/components/logout-button.tsx
+++ b/src/components/logout-button.tsx
@@ -1,33 +1,41 @@
"use client";
import { useRouter } from "next/navigation";
-import { useState } from "react";
+import { useState, useEffect } from "react";
+import { signOut } from "next-auth/react";
+import {
+ clearBasicAuthCredentials,
+ hasBasicAuthCredentials,
+} from "@/lib/client-auth";
/**
- * Client-side logout button for OIDC authentication mode.
+ * Client-side logout button for both OIDC and Basic Auth modes.
*
- * In OIDC mode, clears the session via the API and redirects to login.
- * In Basic Auth mode, this is a no-op since browsers manage credentials.
+ * - OIDC mode: calls NextAuth signOut to clear session cookie
+ * - Basic Auth mode: clears stored credentials from sessionStorage
*/
-export function LogoutButton({
- onLogout,
- className,
-}: {
- onLogout?: () => void;
- className?: string;
-}) {
+export function LogoutButton({ className }: { className?: string }) {
const router = useRouter();
const [loading, setLoading] = useState(false);
+ const [hasCredentials, setHasCredentials] = useState(false);
+
+ useEffect(() => {
+ setHasCredentials(hasBasicAuthCredentials());
+ }, []);
const handleLogout = async () => {
setLoading(true);
try {
- await fetch("/api/auth/logout", { method: "POST" });
+ if (hasCredentials) {
+ clearBasicAuthCredentials();
+ } else {
+ await signOut({ redirect: false });
+ }
router.push("/login");
router.refresh();
- onLogout?.();
} catch {
- // If logout API fails, clear session and redirect anyway
+ // If logout fails, clear credentials anyway and redirect
+ clearBasicAuthCredentials();
router.push("/login");
router.refresh();
} finally {
diff --git a/src/components/mobile-nav.tsx b/src/components/mobile-nav.tsx
index 8a8834d..d9b3119 100644
--- a/src/components/mobile-nav.tsx
+++ b/src/components/mobile-nav.tsx
@@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
import Link from "next/link";
import { getClientVersionLabel } from "@/lib/version-client";
import { ThemeToggle } from "./theme-toggle";
+import { AuthControls } from "./auth-controls";
const navLinks = [
{ href: "/", label: "Overview" },
@@ -73,7 +74,10 @@ export function MobileNav() {
))}
{getClientVersionLabel()}
-
+
)}