feat(oidc): update settings dialog with OIDC user handling and logout flow

This commit is contained in:
SnapOtter
2026-05-13 19:06:22 +08:00
parent 44f42c6866
commit 42265af47b
@@ -28,7 +28,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { Fragment, useCallback, useEffect, useMemo, useState } from "react"; import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiDelete, apiGet, apiPost, apiPut, clearToken } from "@/lib/api"; import { apiDelete, apiGet, apiPost, apiPut, clearToken, formatHeaders } from "@/lib/api";
import { cn, copyToClipboard } from "@/lib/utils"; import { cn, copyToClipboard } from "@/lib/utils";
import { useAnalyticsStore } from "@/stores/analytics-store"; import { useAnalyticsStore } from "@/stores/analytics-store";
import { useSettingsStore } from "@/stores/settings-store"; import { useSettingsStore } from "@/stores/settings-store";
@@ -194,6 +194,10 @@ interface UserEntry {
username: string; username: string;
role: string; role: string;
team: string; team: string;
authProvider?: string;
email?: string;
hasLocalPassword?: boolean;
hasOidcLink?: boolean;
createdAt: string; createdAt: string;
} }
@@ -235,10 +239,25 @@ function GeneralSection() {
]).finally(() => setLoading(false)); ]).finally(() => setLoading(false));
}, []); }, []);
const handleLogout = () => { const handleLogout = async () => {
try {
const res = await fetch("/api/auth/logout", {
method: "POST",
headers: formatHeaders(),
});
const data = await res.json().catch(() => ({}));
clearToken();
localStorage.removeItem("snapotter-username");
if (data.logoutUrl) {
window.location.href = data.logoutUrl;
} else {
window.location.href = "/login";
}
} catch {
clearToken(); clearToken();
localStorage.removeItem("snapotter-username"); localStorage.removeItem("snapotter-username");
window.location.href = "/login"; window.location.href = "/login";
}
}; };
const handleSave = useCallback(async () => { const handleSave = useCallback(async () => {
@@ -1078,6 +1097,16 @@ function PeopleSection() {
{u.username.charAt(0).toUpperCase()} {u.username.charAt(0).toUpperCase()}
</div> </div>
<span className="text-sm font-medium text-foreground truncate">{u.username}</span> <span className="text-sm font-medium text-foreground truncate">{u.username}</span>
{u.hasOidcLink && u.hasLocalPassword !== false && (
<span className="ml-1.5 text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">
Local + OIDC
</span>
)}
{u.hasOidcLink && u.hasLocalPassword === false && (
<span className="ml-1.5 text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">
OIDC
</span>
)}
</div> </div>
{/* Role badge */} {/* Role badge */}
@@ -1130,6 +1159,7 @@ function PeopleSection() {
<Pencil className="h-3.5 w-3.5" /> <Pencil className="h-3.5 w-3.5" />
Edit Role / Team Edit Role / Team
</button> </button>
{u.hasLocalPassword !== false && (
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
@@ -1142,6 +1172,7 @@ function PeopleSection() {
<RotateCcw className="h-3.5 w-3.5" /> <RotateCcw className="h-3.5 w-3.5" />
Reset Password Reset Password
</button> </button>
)}
<div className="border-t border-border my-1" /> <div className="border-t border-border my-1" />
<button <button
type="button" type="button"