Refactor authentication logic and API client
- Update AuthProvider with improved error handling and token management - Enhance API client with better request/response handling - Add comprehensive test coverage for auth flows - Update documentation with current tasks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -25,7 +25,7 @@ import {
|
||||
} from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import * as authApi from "../api/auth";
|
||||
import { SESSION_EXPIRED_EVENT } from "../api/client";
|
||||
import { setUnauthorizedHandler } from "../api/client";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Context
|
||||
@@ -75,9 +75,11 @@ export function AuthProvider({
|
||||
}, [navigate]);
|
||||
|
||||
useEffect((): (() => void) => {
|
||||
window.addEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);
|
||||
setUnauthorizedHandler((): void => {
|
||||
handleSessionExpired();
|
||||
});
|
||||
return (): void => {
|
||||
window.removeEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);
|
||||
setUnauthorizedHandler(null);
|
||||
};
|
||||
}, [handleSessionExpired]);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { type ReactElement } from "react";
|
||||
import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
|
||||
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
|
||||
import { AuthProvider } from "../AuthProvider";
|
||||
import { SESSION_EXPIRED_EVENT } from "../../api/client";
|
||||
import * as clientModule from "../../api/client";
|
||||
|
||||
function CurrentLocation(): ReactElement {
|
||||
const location = useLocation();
|
||||
@@ -17,9 +17,35 @@ describe("AuthProvider", () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("clears auth state and redirects to /login when session-expired fires", async () => {
|
||||
it("registers unauthorized handler on mount and clears on unmount", () => {
|
||||
const setSpy = vi.spyOn(clientModule, "setUnauthorizedHandler");
|
||||
|
||||
const { unmount } = render(
|
||||
<FluentProvider theme={webLightTheme}>
|
||||
<MemoryRouter initialEntries={["/private"]}>
|
||||
<AuthProvider>
|
||||
<Routes>
|
||||
<Route path="*" element={<CurrentLocation />} />
|
||||
</Routes>
|
||||
</AuthProvider>
|
||||
</MemoryRouter>
|
||||
</FluentProvider>,
|
||||
);
|
||||
|
||||
expect(setSpy).toHaveBeenCalledWith(expect.any(Function));
|
||||
|
||||
unmount();
|
||||
expect(setSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it("calls handler to clear auth state and redirect to /login", async () => {
|
||||
sessionStorage.setItem("bangui_authenticated", "true");
|
||||
|
||||
let capturedHandler: (() => void) | null = null;
|
||||
vi.spyOn(clientModule, "setUnauthorizedHandler").mockImplementation((handler) => {
|
||||
capturedHandler = handler;
|
||||
});
|
||||
|
||||
render(
|
||||
<FluentProvider theme={webLightTheme}>
|
||||
<MemoryRouter initialEntries={["/private"]}>
|
||||
@@ -32,7 +58,10 @@ describe("AuthProvider", () => {
|
||||
</FluentProvider>,
|
||||
);
|
||||
|
||||
window.dispatchEvent(new Event(SESSION_EXPIRED_EVENT));
|
||||
// Invoke the handler that was captured during render
|
||||
if (typeof capturedHandler === "function") {
|
||||
capturedHandler();
|
||||
}
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("location")).toHaveTextContent("/login");
|
||||
|
||||
Reference in New Issue
Block a user