From ced1b7084dc118f741e927caa98315fb4f69c636 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Wed, 10 Jun 2026 23:22:44 +0900 Subject: [PATCH] =?UTF-8?q?wr:=20refresh=20token=20=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wr/src/entities/auth/api/login-service.ts | 41 ++++++++----------- .../wr/src/entities/auth/lib/auth-wrapper.tsx | 25 ++--------- .../wr/src/entities/auth/lib/token-manager.ts | 35 ++++------------ 3 files changed, 30 insertions(+), 71 deletions(-) diff --git a/apps/wr/src/entities/auth/api/login-service.ts b/apps/wr/src/entities/auth/api/login-service.ts index 1ad5f2cd0..94be8b53f 100644 --- a/apps/wr/src/entities/auth/api/login-service.ts +++ b/apps/wr/src/entities/auth/api/login-service.ts @@ -1,7 +1,7 @@ import type { LoginService, ILogin } from "../model/login-interface"; import { tokenManager } from "../lib/token-manager"; -import { ACCESS_TOKEN, AUTH_USER_ID, SITE_ID, CLAIM_NAME, ENDPOINT } from "../../../shared/config"; +import {PROXY_HOST, ACCESS_TOKEN, AUTH_USER_ID,SITE_ID, CLAIM_NAME, ENDPOINT, JWT_EXPIRATION_TIME } from "../../../shared/config"; export class LoginApiService implements LoginService { private static instance: LoginApiService; @@ -18,33 +18,31 @@ export class LoginApiService implements LoginService { public async login(payload: ILogin): Promise { try { tokenManager.clearAuthToken(); - const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, { + const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, { method: "POST", headers: { "Content-Type": "application/json", + "X-Site-Id": SITE_ID, }, body: JSON.stringify({ userId: payload.userId, password: payload.password, }), }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message ?? "Login failed"); - } - - const accessToken = data[ACCESS_TOKEN]; - const tokenId = data[AUTH_USER_ID]; + // const data = response.headers.get(ACCESS_TOKEN); + const accessToken = response.headers.get(ACCESS_TOKEN); + const tokenId = response.headers.get(AUTH_USER_ID); if (!accessToken || !tokenId) { throw new Error("Missing required authentication tokens"); } - tokenManager.setToken(CLAIM_NAME, accessToken); - tokenManager.setToken(AUTH_USER_ID, tokenId); - tokenManager.setToken("site-id-key", SITE_ID); - tokenManager.setupRefreshTimer(() => this.silentRefresh()); + tokenManager.setToken(CLAIM_NAME, accessToken) + tokenManager.setToken(AUTH_USER_ID, tokenId) + tokenManager.setToken("site-id-key", SITE_ID) + tokenManager.setupRefreshTimer( + () => this.silentRefresh(), + JWT_EXPIRATION_TIME, + ); } catch (error) { throw new Error(`Login failed: ${error}`); @@ -56,19 +54,17 @@ export class LoginApiService implements LoginService { console.log(' silentRefresh ') try { - const response = await fetch( - `/api/proxy${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, + const response = await fetch(PROXY_HOST+ + `${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`, { method: "PUT", + headers: { + "X-Site-Id": SITE_ID, + }, }, ); const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message ?? "Silent refresh failed"); - } - const accessToken = data[ACCESS_TOKEN]; const tokenId = data[AUTH_USER_ID]; @@ -78,7 +74,6 @@ export class LoginApiService implements LoginService { tokenManager.setToken(CLAIM_NAME, accessToken); tokenManager.setToken(AUTH_USER_ID, tokenId); - tokenManager.setupRefreshTimer(() => this.silentRefresh()); } catch (error) { tokenManager.clearAuthToken(); throw new Error(`Silent refresh failed: ${error}`); diff --git a/apps/wr/src/entities/auth/lib/auth-wrapper.tsx b/apps/wr/src/entities/auth/lib/auth-wrapper.tsx index 91073c42c..796cd44a9 100644 --- a/apps/wr/src/entities/auth/lib/auth-wrapper.tsx +++ b/apps/wr/src/entities/auth/lib/auth-wrapper.tsx @@ -2,12 +2,10 @@ import { useEffect } from "react"; import { usePathname } from "next/navigation"; -import { LoginApi } from "../api/login-service"; -import { tokenManager } from "./token-manager"; import { client } from "../../../shared/api"; import type { IUserInfo } from "../../../shared/store"; import { useUserStore } from "../../../shared/lib"; -import { ENDPOINT, AUTH_USER_ID, CLAIM_NAME } from "../../../shared/config"; +import { ENDPOINT, AUTH_USER_ID } from "../../../shared/config"; import { isAtLoginRedirectUrl } from "@common/network"; import { getCookie } from "../../../shared/lib"; @@ -19,37 +17,22 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { const { userInfo, setUserInfo } = useUserStore((state) => state); async function authCheck() { - // 현재 페이지가 로그인 페이지이면 authCheck(쿠키점검) 안함. if (isAtLoginRedirectUrl(LOGIN_PAGE_URL)) { return; } const userId = getCookie(AUTH_USER_ID); - if (!userId) { // token_id 가 없으면 로그인 페이지로 이동 + if (!userId) { throw new Error("Missing required authentication tokens"); } - if (userInfo) return; - const response = await client.get( `${ENDPOINT.USER_SERVICE.USERS}/info/me`, ); - setUserInfo(response.data); + if (!userInfo) setUserInfo(response.data); } useEffect(() => { - const token = getCookie(CLAIM_NAME); - const userId = getCookie(AUTH_USER_ID); - - if (!token || !userId) { - return; - } - - tokenManager.setupRefreshTimer(() => LoginApi.silentRefresh()); - }, []); - - useEffect(() => { - const isJwtTestPage = pathname?.startsWith("/jwttest"); (async () => { try { await authCheck(); @@ -57,7 +40,7 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { console.error(error); setUserInfo(null); - if (isAtLoginRedirectUrl(LOGIN_PAGE_URL) || isJwtTestPage) return; + if (isAtLoginRedirectUrl(LOGIN_PAGE_URL)) return; // const hostname = window.location.hostname; diff --git a/apps/wr/src/entities/auth/lib/token-manager.ts b/apps/wr/src/entities/auth/lib/token-manager.ts index af6ecb1c3..919386622 100644 --- a/apps/wr/src/entities/auth/lib/token-manager.ts +++ b/apps/wr/src/entities/auth/lib/token-manager.ts @@ -1,4 +1,4 @@ -import { AUTH_USER_ID, CLAIM_NAME, REFRESH_TOKEN } from "@/src/shared/config"; +import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config"; import { getCookie, removeCookie, setCookie } from "@/src/shared/lib/cookie"; type JwtPayload = { @@ -11,7 +11,6 @@ type JwtPayload = { export class TokenManager { private static instance: TokenManager; private refreshTimer: NodeJS.Timeout | null = null; - private static readonly REFRESH_BUFFER_MS = 60000; // 60초 private constructor() {} @@ -80,37 +79,20 @@ export class TokenManager { removeCookie(key); } - private handleRefreshFailure(error: unknown, message: string): void { - console.error(message, error); - this.clearAuthToken(); - } - - public setupRefreshTimer(callback: () => Promise): void { + public setupRefreshTimer( + callback: () => Promise, + expirationTime: number, + ): void { if (this.refreshTimer) { clearTimeout(this.refreshTimer); - this.refreshTimer = null; } - const remainingMs = this.getRemainingMsFromToken(CLAIM_NAME); - if (remainingMs === null) { - return; - } - - if (remainingMs <= 0) { - this.clearAuthToken(); - return; - } - - const refreshDelay = Math.max( - remainingMs - TokenManager.REFRESH_BUFFER_MS, - 0, - ); - this.refreshTimer = setTimeout(() => { callback().catch((error) => { - this.handleRefreshFailure(error, "Silent refresh failed:"); + console.error("Silent refresh failed:", error); + this.clearAuthToken(); }); - }, refreshDelay); + }, expirationTime - 60000); } public clearAuthToken(): void { @@ -120,7 +102,6 @@ export class TokenManager { } this.removeToken(CLAIM_NAME); - this.removeToken(REFRESH_TOKEN); this.removeToken(AUTH_USER_ID); } }