Skip to content

Commit 332b4e3

Browse files
athul-rsclaude
andauthored
UN-4616 [FIX] Leave the page for logout so its redirects complete (#2430)
useLogout called /api/v1/logout with axios. The XHR followed the backend's redirects to the identity provider's logout, which sends no CORS headers, so it failed and flashed an error before reloading. Navigating the page lets the browser follow them to /landing. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 962196f commit 332b4e3

2 files changed

Lines changed: 42 additions & 14 deletions

File tree

‎frontend/src/hooks/useLogout.js‎

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,29 @@
1-
import axios from "axios";
21
import Cookies from "js-cookie";
32
import { usePostHog } from "posthog-js/react";
43

54
import { getSessionData } from "../helpers/GetSessionData";
65
import { getBaseUrl } from "../helpers/GetStaticData";
7-
import { useAlertStore } from "../store/alert-store";
86
import { useSessionStore } from "../store/session-store";
9-
import { useExceptionHandler } from "./useExceptionHandler";
107

118
function useLogout() {
129
const setSessionDetails = useSessionStore((state) => state.setSessionDetails);
1310
const setLogoutLoading = useSessionStore((state) => state.setLogoutLoading);
1411
const posthog = usePostHog();
15-
const { setAlertDetails } = useAlertStore();
16-
const handleException = useExceptionHandler();
1712

1813
const clearSessionCookies = () => {
1914
Cookies.remove("sessionid");
2015
Cookies.remove("csrftoken");
2116
};
2217

23-
return async () => {
18+
return () => {
2419
setLogoutLoading(true);
2520
setSessionDetails(getSessionData(null));
2621
posthog.reset();
2722
clearSessionCookies();
28-
const baseUrl = getBaseUrl();
29-
try {
30-
await axios.get(baseUrl + "/api/v1/logout");
31-
} catch (error) {
32-
setAlertDetails(handleException(error));
33-
}
34-
setLogoutLoading(false);
35-
window.location.href = baseUrl + "/";
23+
// A page navigation, not a request: the backend redirects to the identity
24+
// provider's logout, which answers no CORS request from this origin, and
25+
// the provider sends the browser on to /landing.
26+
window.location.href = `${getBaseUrl()}/api/v1/logout`;
3627
};
3728
}
3829

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { renderHook } from "@testing-library/react";
2+
import { afterEach, describe, expect, it, vi } from "vitest";
3+
4+
import { useSessionStore } from "../store/session-store";
5+
6+
vi.mock("posthog-js/react", () => ({
7+
usePostHog: () => ({ reset: vi.fn() }),
8+
}));
9+
10+
const { default: useLogout } = await import("./useLogout.js");
11+
12+
describe("useLogout", () => {
13+
const original = window.location;
14+
15+
afterEach(() => {
16+
Object.defineProperty(window, "location", {
17+
configurable: true,
18+
value: original,
19+
});
20+
});
21+
22+
// The backend's redirects end at the identity provider, which answers no
23+
// CORS request: only a page navigation can follow them to /landing.
24+
it("leaves the page for the backend logout", () => {
25+
const location = { href: "https://app.example/org/dashboard" };
26+
Object.defineProperty(window, "location", {
27+
configurable: true,
28+
value: location,
29+
});
30+
31+
const { result } = renderHook(() => useLogout());
32+
result.current();
33+
34+
expect(location.href).toMatch(/\/api\/v1\/logout$/);
35+
expect(useSessionStore.getState().isLogoutLoading).toBe(true);
36+
});
37+
});

0 commit comments

Comments
 (0)