fix: auth refresh token issue
This commit is contained in:
parent
0ef5b9b1ea
commit
4f983387ac
|
|
@ -17,7 +17,7 @@ test("login loads backend projects list on /projects", async ({ context, page })
|
|||
},
|
||||
]);
|
||||
|
||||
await page.route("**/api/auth/refresh", async (route) => {
|
||||
await page.route("**/api/auth/user/refresh", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ async function mockAuthAndCloudEditorApis({
|
|||
page: Page;
|
||||
withPutRoute?: boolean;
|
||||
}) {
|
||||
await page.route("**/api/auth/refresh", async (route: Route) => {
|
||||
await page.route("**/api/auth/user/refresh", async (route: Route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import { BackendAuthError, backendAuthApi } from "./api-client";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
const originalBaseUrl = process.env.NEXT_PUBLIC_AUTH_API_BASE_URL;
|
||||
|
||||
describe("backend auth api client", () => {
|
||||
beforeEach(() => {
|
||||
process.env.NEXT_PUBLIC_AUTH_API_BASE_URL = "http://localhost:3001/api";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
if (typeof originalBaseUrl === "string") {
|
||||
process.env.NEXT_PUBLIC_AUTH_API_BASE_URL = originalBaseUrl;
|
||||
} else {
|
||||
delete process.env.NEXT_PUBLIC_AUTH_API_BASE_URL;
|
||||
}
|
||||
});
|
||||
|
||||
test("refresh uses POST /auth/user/refresh and unwraps data payload", async () => {
|
||||
let requestUrl = "";
|
||||
let requestMethod = "";
|
||||
let requestCredentials = "";
|
||||
|
||||
globalThis.fetch = (async (input, init) => {
|
||||
requestUrl = String(input);
|
||||
requestMethod = init?.method ?? "GET";
|
||||
requestCredentials = String(init?.credentials ?? "");
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
status: "success",
|
||||
data: {
|
||||
token: "jwt-access-token",
|
||||
user: {
|
||||
id: "1",
|
||||
email: "user@example.com",
|
||||
},
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 201,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
}) as typeof fetch;
|
||||
|
||||
const response = await backendAuthApi.refresh();
|
||||
|
||||
expect(requestUrl).toBe("http://localhost:3001/api/auth/user/refresh");
|
||||
expect(requestMethod).toBe("POST");
|
||||
expect(requestCredentials).toBe("include");
|
||||
expect(response.token).toBe("jwt-access-token");
|
||||
expect(response.user.email).toBe("user@example.com");
|
||||
});
|
||||
|
||||
test("throws BackendAuthError when backend returns wrapped error in 2xx", async () => {
|
||||
globalThis.fetch = (async () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
status: "error",
|
||||
message: "Invalid refresh token",
|
||||
statusCode: 401,
|
||||
}),
|
||||
{
|
||||
status: 201,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
)) as typeof fetch;
|
||||
|
||||
const refreshPromise = backendAuthApi.refresh();
|
||||
|
||||
await expect(refreshPromise).rejects.toBeInstanceOf(BackendAuthError);
|
||||
await expect(refreshPromise).rejects.toMatchObject({
|
||||
message: "Invalid refresh token",
|
||||
statusCode: 401,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -44,6 +44,17 @@ export class BackendAuthError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
const isWrappedErrorPayload = (
|
||||
payload: unknown,
|
||||
): payload is { status: "error"; message?: string; statusCode?: number } => {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const candidate = payload as { status?: unknown };
|
||||
return candidate.status === "error";
|
||||
};
|
||||
|
||||
const unwrapSuccessPayload = <T,>(payload: TAuthSuccessPayload<T>): T => {
|
||||
if (payload && typeof payload === "object" && "data" in payload) {
|
||||
return payload.data;
|
||||
|
|
@ -100,6 +111,15 @@ async function request<T>({
|
|||
payload = null;
|
||||
}
|
||||
|
||||
if (isWrappedErrorPayload(payload)) {
|
||||
const { message, statusCode } = extractError({
|
||||
payload,
|
||||
defaultMessage: "Request failed",
|
||||
defaultStatusCode: response.status,
|
||||
});
|
||||
throw new BackendAuthError({ message, statusCode });
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const { message, statusCode } = extractError({
|
||||
payload,
|
||||
|
|
@ -148,8 +168,8 @@ export const backendAuthApi = {
|
|||
}),
|
||||
refresh: () =>
|
||||
request<{ token: string; user: TBackendUser }>({
|
||||
path: "/auth/refresh",
|
||||
method: "GET",
|
||||
path: "/auth/user/refresh",
|
||||
method: "POST",
|
||||
}),
|
||||
logout: () =>
|
||||
request<{ message?: string }>({
|
||||
|
|
|
|||
Loading…
Reference in New Issue