import { expect, test } from "@playwright/test"; import { getPersistedOidcUser, installDevApiMock, seedAuth, } from "./helpers/devfront-fixtures"; import { captureEvidence } from "./helpers/evidence"; test.describe("DevFront refresh token renewal", () => { test.afterEach(async ({ page }, testInfo) => { if (testInfo.status === "passed") { await captureEvidence(page, testInfo, testInfo.title); } }); test.beforeEach(async ({ page }) => { await seedAuth(page, { expiresInSeconds: 60, refreshToken: "playwright-refresh-token", state: { returnTo: "/clients" }, }); await installDevApiMock(page, { clients: [], consents: [], auditLogs: [], users: [], tenants: [], }); }); test("exchanges the refresh token for a new access token on silent renewal", async ({ page, }) => { let tokenRequestBody = ""; let authorizeRequested = false; await page.route("**/oidc/token", async (route) => { const request = route.request(); tokenRequestBody = request.postData() ?? ""; await route.fulfill({ status: 200, contentType: "application/json", headers: { "Access-Control-Allow-Origin": "*" }, body: JSON.stringify({ access_token: "rotated-access-token", expires_in: 3600, refresh_token: "rotated-refresh-token", scope: "openid offline_access profile email", session_state: "rotated-session-state", token_type: "Bearer", }), }); }); await page.route("**/oidc/auth**", async (route) => { authorizeRequested = true; await route.fulfill({ status: 500, body: "unexpected authorize request", }); }); await page.goto("/clients"); await expect(page.getByRole("link", { name: "Clients" })).toBeVisible(); const tokenRequestPromise = page.waitForRequest( (request) => request.url().endsWith("/oidc/token") && request.method() === "POST", ); await page.getByRole("button", { name: "Open account menu" }).click(); await page.getByRole("menuitem", { name: "My Profile" }).click(); const tokenRequest = await tokenRequestPromise; const tokenParams = new URLSearchParams(tokenRequestBody); expect(tokenParams.get("grant_type")).toBe("refresh_token"); expect(tokenParams.get("refresh_token")).toBe("playwright-refresh-token"); await expect(page.getByRole("heading", { name: "내 정보" })).toBeVisible(); await expect .poll(async () => { const storedUser = await getPersistedOidcUser(page); return storedUser?.access_token; }) .toBe("rotated-access-token"); await expect .poll(async () => { const storedUser = await getPersistedOidcUser(page); return storedUser?.refresh_token; }) .toBe("rotated-refresh-token"); expect(tokenRequest.url()).toContain("/oidc/token"); expect(authorizeRequested).toBe(false); }); });