Files
TJWaterFrontend_Refine/src/lib/logoutFlow.ts
T
jiang d4c8fc9db8
Generic Container CI/CD / test-build-publish (push) Successful in 1m4s
Frontend CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m4s
fix(auth): preserve intentional logout across browser return
Keep a bounded per-tab logout intent and broadcast it to already open tabs so back navigation after a normal logout is not mistaken for renewal failure. Preserve real refresh and session expiry signals, and keep IdP navigation reachable when local cleanup fails.
2026-11-04 14:34:18 +08:00

55 lines
1.2 KiB
TypeScript

import { markIntentionalLogout } from "@/lib/logoutIntent";
const KEYCLOAK_LOGOUT_PATH = "/api/auth/keycloak-logout";
type CompleteLogoutOptions = {
reportAudit: () => Promise<unknown>;
clearLocalState: () => void;
navigate: (path: string) => void;
};
type LogoutAuditOptions = {
endpoint: string;
accessToken?: string | null;
fetcher?: typeof fetch;
};
export const reportLogoutAudit = ({
endpoint,
accessToken,
fetcher = fetch,
}: LogoutAuditOptions) => {
const headers = new Headers({ "Content-Type": "application/json" });
if (accessToken) {
headers.set("Authorization", `Bearer ${accessToken}`);
}
return fetcher(endpoint, {
method: "POST",
headers,
body: JSON.stringify({ event: "logout" }),
keepalive: true,
});
};
export const completeLogout = async ({
reportAudit,
clearLocalState,
navigate,
}: CompleteLogoutOptions) => {
try {
void reportAudit().catch(() => undefined);
} catch {
// Synchronous reporting errors must not block logout either.
}
markIntentionalLogout();
try {
clearLocalState();
} catch {
// Browser storage cleanup failures must not prevent the IdP logout.
}
navigate(KEYCLOAK_LOGOUT_PATH);
return { success: true as const };
};