Loading the catalog…
Loading the catalog…
velog
"탭 A에서는 관리자 계정으로 작업 중인데, 탭 B에서 테스트용 학생 계정으로 로그인했더니 탭 A의 화면이 관리자 권한 그대로 남아있다?!" XSS 방어를 위해 Access Token은 탭 메모리 에 두고, Refresh Token은 HttpOnly 쿠키 로 관리하는 모던 웹 인증 아키텍처에서 필연적으로 마주치는 '멀티 탭 세션 불일치' 와 '토큰 회전(RTR) 동시성 경합' 을 해결한 실전 트러블슈팅 기록입니다. 🚨 1. 증상: 탭 사이에서 엇갈리는 계정의 유령 사용자(특히 관리자/매니저)가 여러 탭을 띄워두고 작업할 때 다음과 같은 기묘하고 위험한 현상이 발견되었습니다: [시나리오] 1. 탭 A: '관리자'로 로그인 ➡️ 관리자 전용 대시보드 조회 중 2. 탭 B: 테스트를 위해 로그아웃 후 '일반 학생' 계정으로 로그인 3. 결과: 탭 A는 여전히 관리자 화면과 권한이 그대로 유지됨! 더 심각한 문제는 Access Token 만료 시점(30분 뒤) 에 터졌습니다. 탭 A의 메모리에 있던 옛 Access Token이 만료되자, 백그라운드에서 /auth/refresh 를 호출합니다. 이때 브라우저는 공유 쿠키에 들어있는 '학생 계정의 Refresh Token' 을 서버로 실어 보냅니다. 그 결과, 탭 A의 화면은 여전히 '관리자' 화면인데, API 요청은 '학생' 토큰으로 나가며 권한 오류(403)가 터지거나 화면과 실제 요청 주체가 뒤죽박죽 꼬이는 현상이 발생했습니다. 🔍 2. 근본 원인: 메모리 토큰과 공유 쿠키의 태생적 간극 보안을 위해 설계한 아키텍처 자체가 원인이었습니다: Access Token & User State : 각 탭의 독립된 JS 메모리( AuthContext ) 에만 존재합니다. Refresh Token : 브라우저의 모든 탭이 공유하는 HttpOnly 쿠키 에 존재합니다. 탭 B에서 로그인/로그아웃을 해도 탭 A는 이 사실을 전혀 전달받지 못하므로, 최대 30분 동안 옛날 계정의 신분(Identity)을 붙잡고 있었던 것입니다. ⚠️ 또 다른 복병: Refresh Token Rotation (RTR) 동시성 폭탄 우리 백엔드는 보안을 위해 토큰 갱신 시마다 리프레시 토큰을 교체(Rotate)하고, 이미 사용된 옛날 토큰이 들어오면 '토큰 탈취'로 간주하여 사용자의 모든 세션을 강제 파기 하는 정책을 가지고 있었습니다. 만약 여러 탭이 동시에 켜진 상태에서 *"다른 탭 로그인 시 모든 탭이 각자 refresh를 호출하게 하자"*라고 단순하게 접근하면: 탭 A, B, C가 동시에 /auth/refresh 를 호출 ➡️ 찰나의 차이로 늦게 도착한 요청이 이전 토큰을 들고 감 ➡️ 서버가 탈취로 오인해 모든 탭을 강제 로그아웃시키는 참사 로 이어집니다. 🛠️ 3. 해결 방안: 브라우저 최신 표준 API 2가지의 조합 서버 코드는 단 한 줄도 건드리지 않고, 브라우저 표준 Web API인 BroadcastChannel 과 Web Locks API 를 도입하여 문제를 깔끔하게 종결지었습니다. 1 BroadcastChannel 로 탭 간 실시간 신분 동기화 AuthContext 에 브로드캐스트 채널을 연결하여, 로그인/로그아웃/회원전환이 일어날 때마다 모든 탭에 확성기를 켭니다: // frontend/src/contexts/AuthContext.tsx const authChannel = new BroadcastChannel('auth-sync-channel'); export const AuthProvider = ({ children }) => { // 1. 로그인/로그아웃 시 다른 탭으로 전파 const login = (token: string, user: User) => { setAccessToken(token); setUser(user); authChannel.postMessage({ type: 'LOGIN', accessToken: token, user }); }; const logout = () => { resetAuthState(); authChannel.postMessage({ type: 'LOGOUT' }); }; // 2. 다른 탭에서 날아온 메시지 수신 useEffect(() => { authChannel.onmessage = (event) => { const { type, accessToken, user } = event.data; if (type === 'LOGIN') { // 다른 탭이 각자 refresh를 때리지 않고, 받은 최신 토큰/유저로 즉시 교체! setAccessToken(accessToken); setUser(user); } else if (type === 'LOGOUT') { resetAuthState(); } }; }, []); }; 핵심 포인트 : 다른 탭이 직접 서버에 refresh 를 요청하게 만들지 않고, 로그인 성공 탭이 이미 발급받은 새 토큰과 유저 정보를 그대로 복사 전달 함으로써 불필요한 네트워크 요청과 RTR 충돌을 원천 차단했습니다. 2 Web Locks API ( navigator.locks )로 탭 간 토큰 갱신 직렬화 여러 탭이 동시에 켜져 있다가 거의 같은 시점에 Access Token이 만료될 때를 대비해, 갱신 요청을 브라우저 탭들 사이에서 Mutex(상호 배제) 로 묶어주었습니다: // frontend/src/api/apiClient.ts export async function refreshAccessToken(): Promise<string> { // Web Locks API가 지원되는 최신 브라우저 환경 if (navigator.locks) { return await navigator.locks.request('auth-token-refresh-lock', async () => { // 락(Lock)을 획득한 딱 하나의 탭만 네트워크 요청을 보냄 // 이미 다른 탭이 갱신을 끝냈다면 최신 쿠키를 들고 안전하게 진입 return await executeRefreshRequest(); }); } // 미지원 구형 브라우저 폴백 return await executeRefreshRequest(); } 🧪 4. 검증 결과 멀티 탭 동기화 테스트 : 탭 A(관리자 대시보드) 열어둔 상태에서 탭 B에서 학생 로그인 ➡️ 탭 A가 새로고침 없이 즉시 학생 권한으로 동기화되며 관리자 메뉴 자동 소멸 . 탭 B에서 로그아웃 ➡️ 탭 A 즉시 로그인 안내 페이지로 전환 (새로고침 불필요) . 토큰 갱신 레이스 컨디션 테스트 : 5개 탭을 동시에 띄워두고 토큰 만료 시뮬레이션 ➡️ Web Locks 덕분에 1개 탭만 직렬로 갱신을 수행하고, RTR 세션 파기 오류 발생률 0건 달성. 💡 이번 트러블슈팅을 통해 얻은 교훈 메모리 저장소의 맹점 : SPA에서 Access Token을 변수(메모리)에 저장하는 것은 XSS 방어에 훌륭하지만, 멀티 탭 상태 불일치라는 부작용을 동반합니다. 현대 브라우저 API를 적극 활용하라 : 예전에는 localStorage 의 storage 이벤트 꼼수를 썼지만, 지금은 BroadcastChannel 과 navigator.locks 라는 훨씬 우아하고 표준적인 해결책이 존재합니다. 보안(RTR)과 사용자 경험(멀티탭)은 상충하기 쉽지만, 프론트엔드 단계의 세밀한 동기화 설계로 두 마리 토끼를 모두 잡을 수 있습니다.
What RADAR observed and classified to build this opportunity. It is what the source published, not a verification that the offer is still active.
[React] 다른 탭에서 다른 아이디로 로그인했는데 관리자 권한이 유지된다? (BroadcastChannel과 Web Locks로 탭 간 인증 동기화하기). "탭 A에서는 관리자 계정으로 작업 중인데, 탭 B에서 테스트용 학생 계정으로 로그인했더니 탭 A의 화면이 관리자 권한 그대로 남아있다?!" XSS 방어를 위해 Access Token은 탭 메모리 에 두고, Refresh Token은 HttpOnly 쿠키 로 관리하는 모던 웹 인증 아키텍처에서 필연적으로 마주치는 '멀티 탭 세션 불일치' 와 '토큰 회전(RTR) 동시성 경합' 을 해결한 실전 트러블슈팅 기록입니다. 🚨 1. 증상: 탭 사이에서 엇갈리는 계정의 유령 사용자(특히 관리자/매니저)가 여러 탭을 띄워두고 작업할 때 다음과 같은 기묘하고…
Open source