diff --git a/gateway/html/login.html b/gateway/html/login.html index 15440d2..7874a03 100644 --- a/gateway/html/login.html +++ b/gateway/html/login.html @@ -201,8 +201,20 @@ } } + // logout=1 파라미터가 있으면 모든 인증 데이터 정리 + var isLogout = new URLSearchParams(location.search).get('logout') === '1'; + if (isLogout) { + ssoCookie.remove('sso_token'); + ssoCookie.remove('sso_user'); + ssoCookie.remove('sso_refresh_token'); + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { + localStorage.removeItem(k); + }); + } + // 이미 로그인 되어있으면 포털로 (쿠키 또는 localStorage 체크 + 만료 확인) - var existingToken = ssoCookie.get('sso_token') || localStorage.getItem('sso_token'); + var existingToken = isLogout ? null : (ssoCookie.get('sso_token') || localStorage.getItem('sso_token')); if (existingToken && existingToken !== 'undefined' && existingToken !== 'null') { if (isTokenValid(existingToken)) { // 쿠키 재설정 (localStorage에만 있고 쿠키가 없는 경우 대비) diff --git a/gateway/html/shared/nav-header.js b/gateway/html/shared/nav-header.js index fb27418..f888494 100644 --- a/gateway/html/shared/nav-header.js +++ b/gateway/html/shared/nav-header.js @@ -51,7 +51,7 @@ ['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { localStorage.removeItem(k); }); - window.location.href = this.getLoginUrl(); + window.location.href = this.getLoginUrl(window.location.href) + '&logout=1'; }, /** diff --git a/system1-factory/web/js/api-base.js b/system1-factory/web/js/api-base.js index 322ec5a..432054e 100644 --- a/system1-factory/web/js/api-base.js +++ b/system1-factory/web/js/api-base.js @@ -154,7 +154,7 @@ if ('caches' in window) { // 401 Unauthorized 처리 if (response.status === 401) { window.clearSSOAuth(); - window.location.href = window.getLoginUrl(); + window.location.href = window.getLoginUrl() + '&logout=1'; throw new Error('인증이 만료되었습니다.'); } diff --git a/system1-factory/web/js/auth-check.js b/system1-factory/web/js/auth-check.js index 939c9d0..76b145b 100644 --- a/system1-factory/web/js/auth-check.js +++ b/system1-factory/web/js/auth-check.js @@ -120,8 +120,24 @@ async function checkPageAccess(pageKey) { } } +// 쿠키 직접 읽기 (api-base.js의 cookieGet은 IIFE 내부 함수이므로 접근 불가) +function _authCookieGet(name) { + var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')); + return match ? decodeURIComponent(match[1]) : null; +} + // 즉시 실행 함수로 스코프를 보호하고 로직을 실행 (async function() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + var cookieToken = _authCookieGet('sso_token'); + var localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { localStorage.removeItem(k); }); + window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; + return; + } + if (!isLoggedIn()) { clearAuthData(); // 만약을 위해 한번 더 정리 window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; diff --git a/system2-report/web/js/app-init.js b/system2-report/web/js/app-init.js index 97fc474..765cc53 100644 --- a/system2-report/web/js/app-init.js +++ b/system2-report/web/js/app-init.js @@ -19,6 +19,12 @@ window.location.href = window.getLoginUrl ? window.getLoginUrl() : '/login'; } + // ===== 쿠키 직접 읽기 (api-base.js의 cookieGet은 IIFE 내부이므로) ===== + function cookieGet(name) { + var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')); + return match ? decodeURIComponent(match[1]) : null; + } + // ===== 인증 함수 (api-base.js의 전역 헬퍼 활용) ===== function isLoggedIn() { var token = window.getSSOToken ? window.getSSOToken() : localStorage.getItem('sso_token'); @@ -40,6 +46,16 @@ // ===== 메인 초기화 ===== async function init() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + var cookieToken = cookieGet('sso_token'); + var localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(function(k) { localStorage.removeItem(k); }); + safeRedirectToLogin(); + return; + } + // 1. 인증 확인 if (!isLoggedIn()) { clearAuthData(); diff --git a/system3-nonconformance/web/static/js/app.js b/system3-nonconformance/web/static/js/app.js index a6b8290..bc301d6 100644 --- a/system3-nonconformance/web/static/js/app.js +++ b/system3-nonconformance/web/static/js/app.js @@ -46,8 +46,17 @@ class App { * 인증 확인 */ async checkAuth() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + const cookieToken = this._cookieGet('sso_token'); + const localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); + throw new Error('쿠키 없음 - 로그아웃 상태'); + } + // SSO 쿠키 우선, localStorage 폴백 - const token = this._cookieGet('sso_token') || localStorage.getItem('sso_token'); + const token = cookieToken || localToken; if (!token) { throw new Error('토큰 없음'); } diff --git a/system3-nonconformance/web/static/js/core/auth-manager.js b/system3-nonconformance/web/static/js/core/auth-manager.js index 3d9fd3c..4762bc1 100644 --- a/system3-nonconformance/web/static/js/core/auth-manager.js +++ b/system3-nonconformance/web/static/js/core/auth-manager.js @@ -97,6 +97,15 @@ class AuthManager { * 저장소에서 사용자 정보 복원 (SSO 쿠키 + localStorage) */ restoreUserFromStorage() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + const cookieToken = this._cookieGet('sso_token'); + const localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); + return; + } + const token = this._getToken(); const user = this._getUser(); @@ -240,7 +249,7 @@ class AuthManager { logout() { this.clearAuth(); this.notifyListeners('logout'); - window.location.href = this._getLoginUrl(); + window.location.href = this._getLoginUrl() + '&logout=1'; } /** diff --git a/tkpurchase/web/static/js/tkpurchase-core.js b/tkpurchase/web/static/js/tkpurchase-core.js index 4903eb1..29e3857 100644 --- a/tkpurchase/web/static/js/tkpurchase-core.js +++ b/tkpurchase/web/static/js/tkpurchase-core.js @@ -79,7 +79,7 @@ function doLogout() { if (!confirm('로그아웃?')) return; _cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token'); ['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); - location.href = getLoginUrl(); + location.href = getLoginUrl() + '&logout=1'; } /* ===== Navbar ===== */ @@ -106,6 +106,16 @@ let currentUser = null; /* ===== Init ===== */ function initAuth() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + const cookieToken = _cookieGet('sso_token'); + const localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); + _safeRedirect(); + return false; + } + const token = getToken(); if (!token) { _safeRedirect(); return false; } const decoded = decodeToken(token); diff --git a/tksafety/web/static/js/tksafety-core.js b/tksafety/web/static/js/tksafety-core.js index 17e0faf..38ead95 100644 --- a/tksafety/web/static/js/tksafety-core.js +++ b/tksafety/web/static/js/tksafety-core.js @@ -76,7 +76,7 @@ function doLogout() { if (!confirm('로그아웃?')) return; _cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token'); ['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); - location.href = getLoginUrl(); + location.href = getLoginUrl() + '&logout=1'; } /* ===== Navbar ===== */ @@ -100,6 +100,16 @@ let currentUser = null; /* ===== Init ===== */ function initAuth() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + const cookieToken = _cookieGet('sso_token'); + const localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); + _safeRedirect(); + return false; + } + const token = getToken(); if (!token) { _safeRedirect(); return false; } const decoded = decodeToken(token); diff --git a/user-management/web/static/js/tkuser-core.js b/user-management/web/static/js/tkuser-core.js index 0848e81..8de4b23 100644 --- a/user-management/web/static/js/tkuser-core.js +++ b/user-management/web/static/js/tkuser-core.js @@ -75,7 +75,7 @@ function doLogout() { if (!confirm('로그아웃?')) return; _cookieRemove('sso_token'); _cookieRemove('sso_user'); _cookieRemove('sso_refresh_token'); ['sso_token','sso_user','sso_refresh_token','token','user','access_token','currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); - location.href = getLoginUrl(); + location.href = getLoginUrl() + '&logout=1'; } /* ===== State ===== */ @@ -83,6 +83,16 @@ let currentUser = null; /* ===== Init ===== */ async function init() { + // 쿠키 우선 검증: 쿠키 없고 localStorage에만 토큰이 있으면 정리 + const cookieToken = _cookieGet('sso_token'); + const localToken = localStorage.getItem('sso_token'); + if (!cookieToken && localToken) { + ['sso_token','sso_user','sso_refresh_token','token','user','access_token', + 'currentUser','current_user','userInfo','userPageAccess'].forEach(k => localStorage.removeItem(k)); + _safeRedirect(); + return; + } + const token = getToken(); if (!token) { _safeRedirect(); return; } const decoded = decodeToken(token);