diff --git a/apps/cs/playwright-report/index.html b/apps/cs/playwright-report/index.html index db6ff1e5a..9ac4d2b42 100644 --- a/apps/cs/playwright-report/index.html +++ b/apps/cs/playwright-report/index.html @@ -87,4 +87,4 @@ Error generating stack: `+l.message+`
- \ No newline at end of file + \ No newline at end of file diff --git a/apps/cs/tests/jwt-parse.spec.ts b/apps/cs/tests/jwt-parse.spec.ts new file mode 100644 index 000000000..0e0a543ef --- /dev/null +++ b/apps/cs/tests/jwt-parse.spec.ts @@ -0,0 +1,53 @@ +import { test, expect } from "@playwright/test"; +// 실행방법 npx playwright test tests/jwt-parse.spec.ts +// npx playwright test tests/jwt-parse.spec.ts --project=chromium +// npx playwright test tests/jwt-parse.spec.ts --project=chromium --reporter=list +// npx playwright test tests/jwt-parse.spec.ts --debug + +test.describe("JWT Token Parsing Test", () => { + test("atob 함수를 이용한 JWT Payload 파싱 및 콘솔 출력", async () => { + const token = + "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2NWEwMGY2NS04NDYwLTQ5YWYtOThlYy0wNDI5NzdlNTZmNGIiLCJhdXRob3JpdGllcyI6IlJPTEVfQU5PTllNT1VTLFJPTEVfQURNSU4sUk9MRV9BRE1JTl9BUyIsIm1uZ3JZbiI6IlkiLCJleHAiOjE3ODM1NjMwMzd9.mWZZ1ooiUs-8JbHCv78NEl51c1F6a7849XdTcSoSSrnqKa2SlFjbNoInHNHkXZo0InRXyg-hpFi-8w-IuOwf8g"; + + // 1. JWT 토큰의 2번째 영역인 Payload 추출 + const base64Payload = token.split(".")[1]; + expect(base64Payload).toBeDefined(); + + // 2. atob 함수를 사용하여 Base64 디코딩 실행 + const decodedJsonString = atob(base64Payload!); + + // 3. JSON 객체로 파싱 + const payloadObj = JSON.parse(decodedJsonString); + + // 4. 만료 여부 판단 + const currentTimeMs = Date.now(); + const expiryTimeMs = payloadObj.exp * 1000; + const isExpired = expiryTimeMs < currentTimeMs; + const differenceMs = Math.abs(expiryTimeMs - currentTimeMs); + + // 5. 콘솔 출력 + console.log("\n=================================================="); + console.log("Decoded JWT Payload Object:"); + console.log(JSON.stringify(payloadObj, null, 2)); + console.log("--------------------------------------------------"); + console.log(`사용자 ID (sub): ${payloadObj.sub}`); + console.log(`사용자 권한 (authorities): ${payloadObj.authorities}`); + console.log(`관리자 여부 (mngrYn): ${payloadObj.mngrYn}`); + console.log(`만료 시각 (exp): ${payloadObj.exp}`); + console.log( + `만료 날짜 변환: ${new Date(expiryTimeMs).toLocaleString()}`, + ); + console.log(`현재 시각: ${new Date(currentTimeMs).toLocaleString()}`); + console.log(`만료 여부: ${isExpired ? "만료됨 ❌" : "유효함 ✅"}`); + if (isExpired) { + console.log(`만료 후 경과 시간: ${Math.floor(differenceMs / 1000)}초 경과`); + } else { + console.log(`만료까지 남은 시간: ${Math.floor(differenceMs / 1000)}초 남음`); + } + console.log("==================================================\n"); + + // 간단한 검증 추가 + expect(payloadObj).toHaveProperty("exp"); + expect(payloadObj.mngrYn).toBe("Y"); + }); +}); diff --git a/apps/cs/tests/multi-device-auth.spec.ts b/apps/cs/tests/multi-device-auth.spec.ts index 7b2633baa..f6d47e991 100644 --- a/apps/cs/tests/multi-device-auth.spec.ts +++ b/apps/cs/tests/multi-device-auth.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from "@playwright/test"; // 테스트 순차 실행 보장 (전 단계 상태가 영향을 줄 수 있으므로 serial 모드로 설정) +// npx playwright test tests/multi-device-auth.spec.ts --debug test.describe.configure({ mode: "serial" }); const adminId = process.env.TEST_ADMIN_ID || "admin1gmail"; @@ -34,9 +35,13 @@ function getCookieValue( (h) => h.name.toLowerCase() === "set-cookie", ); for (const header of cookieHeaders) { - const parts = header.value.split(";")[0].split("="); - if (parts[0].trim() === name) { - return parts[1].trim(); + const firstPart = header.value.split(";")[0]; + if (!firstPart) continue; + const parts = firstPart.split("="); + const key = parts[0]?.trim(); + const val = parts[1]?.trim(); + if (key === name) { + return val ?? null; } } return null; @@ -53,7 +58,9 @@ function hasCookieAttribute( ); for (const header of cookieHeaders) { const parts = header.value.split(";"); - const cookieName = parts[0].split("=")[0].trim(); + const firstPart = parts[0]; + if (!firstPart) continue; + const cookieName = firstPart.split("=")[0]!.trim(); if (cookieName === name) { return parts .slice(1) @@ -253,9 +260,9 @@ test.describe("다중 디바이스 인증 & Refresh Token Rotation (RTR) 테스 // 클라이언트의 인증 쿠키들이 소멸되었는지 검증 const state = await pcContext.storageState(); - const refreshCookie = state.cookies.find((c) => c.name === "refresh-token"); - const accessCookie = state.cookies.find((c) => c.name === "access-token"); - const userCookie = state.cookies.find((c) => c.name === "user-id"); + const refreshCookie = state.cookies.find((c: { name: string }) => c.name === "refresh-token"); + const accessCookie = state.cookies.find((c: { name: string }) => c.name === "access-token"); + const userCookie = state.cookies.find((c: { name: string }) => c.name === "user-id"); expect(refreshCookie).toBeUndefined(); expect(accessCookie).toBeUndefined(); @@ -315,7 +322,7 @@ test.describe("다중 디바이스 인증 & Refresh Token Rotation (RTR) 테스 // 실제 브라우저의 HTTP Context 쿠키에는 저장되어 있어야 함 const contextCookies = await page.context().cookies(); - const refreshCookie = contextCookies.find((c) => c.name === "refresh-token"); + const refreshCookie = contextCookies.find((c: { name: string }) => c.name === "refresh-token"); expect(refreshCookie).toBeDefined(); expect(refreshCookie?.httpOnly).toBe(true); });