From c09e457ade07fb8cdad6fad3d7bd592564739ced Mon Sep 17 00:00:00 2001 From: Ahmed Sami Date: Sat, 25 Jan 2025 09:52:25 +0200 Subject: [PATCH 1/2] Redirect users to login page upon 403 on getSelf #656 --- packages/api-client/src/core/client.ts | 17 ++++- packages/api-client/tests/api-client.spec.ts | 68 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 packages/api-client/tests/api-client.spec.ts diff --git a/packages/api-client/src/core/client.ts b/packages/api-client/src/core/client.ts index 67df7df39..6645d53a1 100644 --- a/packages/api-client/src/core/client.ts +++ b/packages/api-client/src/core/client.ts @@ -2,7 +2,22 @@ export class APIClient { constructor(private readonly baseUrl: string) {} async request(url: string, options: RequestInit): Promise { - return await fetch(`${this.baseUrl}${url}`, options) + const response = await fetch(`${this.baseUrl}${url}`, options); + + if (response.status === 403) { + // Clear local data and cookies + localStorage.clear(); + document.cookie.split(";").forEach((cookie) => { + const name = cookie.split("=")[0].trim(); + document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`; + }); + + // Redirect to /auth + window.location.href = "/auth"; + return Promise.reject(new Error("User is not authenticated. Redirecting to /auth.")); + } + + return response; } /** diff --git a/packages/api-client/tests/api-client.spec.ts b/packages/api-client/tests/api-client.spec.ts new file mode 100644 index 000000000..ef35316f4 --- /dev/null +++ b/packages/api-client/tests/api-client.spec.ts @@ -0,0 +1,68 @@ +import { APIClient } from "../src/APIClient"; +import { rest } from "msw"; +import { setupServer } from "msw/node"; + +const backendUrl = process.env.BACKEND_URL +const apiClient = new APIClient(backendUrl); + +// Mock server for API requests +const server = setupServer( + rest.get(`${backendUrl}/test`, (req, res, ctx) => { + return res(ctx.status(200), ctx.json({ message: "Success" })); + }), + rest.get(`${backendUrl}/auth-required`, (req, res, ctx) => { + return res(ctx.status(403), ctx.json({ message: "Forbidden" })); + }) +); + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +describe("APIClient", () => { + beforeEach(() => { + localStorage.clear(); + document.cookie = ""; + }); + + it("should fetch data successfully", async () => { + const response = await apiClient.get("/test"); + const data = await response.json(); + + expect(response.status).toBe(200); + expect(data.message).toBe("Success"); + }); + + it("should clear local data and redirect on 403 Forbidden", async () => { + // Mock window.location.href + delete (window as any).location; + (window as any).location = { href: "" }; + + await expect(apiClient.get("/auth-required")).rejects.toThrow( + "User is not authenticated. Redirecting to /auth." + ); + + // Verify localStorage is cleared + expect(localStorage.length).toBe(0); + + // Verify cookies are cleared + expect(document.cookie).toBe(""); + + // Verify redirection + expect(window.location.href).toBe("/auth"); + }); + + it("should handle other HTTP errors gracefully", async () => { + server.use( + rest.get(`${baseUrl}/error`, (req, res, ctx) => { + return res(ctx.status(500), ctx.json({ message: "Server Error" })); + }) + ); + + const response = await apiClient.get("/error"); + expect(response.status).toBe(500); + + const data = await response.json(); + expect(data.message).toBe("Server Error"); + }); +}); From 388db5ca34fd598c8e78c2066687a46f69aeaf60 Mon Sep 17 00:00:00 2001 From: Ahmed Sami Date: Sat, 25 Jan 2025 13:09:31 +0200 Subject: [PATCH 2/2] now I'm sure the local storage is exsite before I clear and cookies are existing too --- packages/api-client/src/core/client.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/api-client/src/core/client.ts b/packages/api-client/src/core/client.ts index 6645d53a1..c4fe49cce 100644 --- a/packages/api-client/src/core/client.ts +++ b/packages/api-client/src/core/client.ts @@ -6,11 +6,15 @@ export class APIClient { if (response.status === 403) { // Clear local data and cookies - localStorage.clear(); - document.cookie.split(";").forEach((cookie) => { - const name = cookie.split("=")[0].trim(); - document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`; - }); + if (typeof localStorage !== 'undefined') { + localStorage.clear(); + } + if (typeof document !== 'undefined' && typeof document.cookie !== 'undefined') { + document.cookie.split(";").forEach((cookie) => { + const name = cookie.split("=")[0].trim(); + document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`; + }); + } // Redirect to /auth window.location.href = "/auth";