67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
/**
|
|
* JWT 토큰 단일 관리 모듈
|
|
*
|
|
* - localStorage 와 쿠키에 동시 저장 (미들웨어가 쿠키만 읽기 때문)
|
|
* - 만료/만료임박 판단 유틸 제공
|
|
* - 이 파일이 토큰 저장/조회의 단일 진실 공급원 (Single Source of Truth)
|
|
*
|
|
* 기존에 client.ts 와 useAuth.ts 양쪽에 중복 정의되어 있던 TokenManager 를
|
|
* 이 파일로 통합. 양쪽에서 import 해서 사용한다.
|
|
*/
|
|
|
|
const TOKEN_KEY = "authToken";
|
|
const COOKIE_MAX_AGE = 86400; // 24h
|
|
const REFRESH_THRESHOLD_MS = 30 * 60 * 1000; // 만료 30분 전부터 갱신 대상
|
|
|
|
export const TokenManager = {
|
|
getToken: (): string | null => {
|
|
if (typeof window !== "undefined") {
|
|
return localStorage.getItem(TOKEN_KEY);
|
|
}
|
|
return null;
|
|
},
|
|
|
|
setToken: (token: string): void => {
|
|
if (typeof window === "undefined") return;
|
|
localStorage.setItem(TOKEN_KEY, token);
|
|
// 미들웨어에서 사용
|
|
document.cookie = `${TOKEN_KEY}=${token}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
},
|
|
|
|
removeToken: (): void => {
|
|
if (typeof window === "undefined") return;
|
|
localStorage.removeItem(TOKEN_KEY);
|
|
document.cookie = `${TOKEN_KEY}=; path=/; max-age=0; SameSite=Lax`;
|
|
},
|
|
|
|
isTokenExpired: (token: string): boolean => {
|
|
try {
|
|
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
return payload.exp * 1000 < Date.now();
|
|
} catch {
|
|
return true;
|
|
}
|
|
},
|
|
|
|
/** 만료 30분 전부터 true */
|
|
isTokenExpiringSoon: (token: string): boolean => {
|
|
try {
|
|
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
const expiryTime = payload.exp * 1000;
|
|
const currentTime = Date.now();
|
|
return expiryTime - currentTime < REFRESH_THRESHOLD_MS && expiryTime > currentTime;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
getTimeUntilExpiry: (token: string): number => {
|
|
try {
|
|
const payload = JSON.parse(atob(token.split(".")[1]));
|
|
return payload.exp * 1000 - Date.now();
|
|
} catch {
|
|
return 0;
|
|
}
|
|
},
|
|
};
|