// Modified in the independent hardening fork; see docs/hardening/README.md. // Modified for AgentDeskFixes: prevent stale profile updates and logout from modifying a newer session. import { clearSession, readSession, writeSession, type AuthSession } from "@/lib/auth" import { isSameSession } from "@/lib/session-request-guard" import { request } from "@/lib/api/client" export type LoginRequest = { username: string password: string } export type UpdateProfileRequest = { avatar: string email?: string nickname: string } export type ProfileAvatarAsset = { url: string } export async function loginWithPassword(payload: LoginRequest) { const data = await request("/api/auth/login", { method: "POST", body: JSON.stringify(payload), skipAuth: true, }) writeSession(data) return data } export async function exchangeOIDCTicket(ticket: string) { const data = await request("/api/auth/oidc_exchange", { method: "POST", body: JSON.stringify({ ticket }), skipAuth: true, }) writeSession(data) return data } export async function fetchProfile() { return request("/api/auth/profile") } export async function updateProfile(payload: UpdateProfileRequest) { const stored = readSession() const data = await request("/api/auth/profile/update", { method: "POST", body: JSON.stringify(payload), }) const nextSession: AuthSession = { ...data, accessToken: data.accessToken || stored?.accessToken || "", expiresAt: data.expiresAt || stored?.expiresAt, } if (isSameSession(stored, readSession())) writeSession(nextSession) return nextSession } export function uploadProfileAvatar(file: File) { const formData = new FormData() formData.set("file", file) return request("/api/auth/profile/avatar/upload", { method: "POST", body: formData, }) } export async function logout() { const stored = readSession() try { await request("/api/auth/logout", { method: "POST", }) } finally { if (isSameSession(stored, readSession())) clearSession() } }