You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
소셜 로그인(Google/Firebase) 직후 홈 화면 진입 시 보호된 API 호출이 즉시 401을 반환하여 클라이언트가 강제 로그아웃 처리하는 현상이 발생합니다.
/api/auth/login은 정상 200, accessToken/refreshToken 발급도 정상이지만, 발급된 토큰으로 /api/todos 등 인증 필요 엔드포인트 호출 시 401 응답이 반환되며 응답 body가 비어 있습니다.
클라이언트가 401을 토큰 만료로 간주해 reissue를 시도 → reissue도 200 성공 → 새 토큰으로 retry → 또 401 → 강제 로그아웃 → /login 으로 복귀합니다.
결과적으로 어떤 사용자도 정상 로그인 직후 홈 진입을 유지할 수 없는 차단성 버그입니다.
🔄 재현 방법
Flutter 앱(iOS Simulator)에서 Google 소셜 로그인 수행
백엔드 로그인 응답 200 수신 후 /home 진입
/home 초기 진입 시 자동 호출되는 GET /api/todos가 401 반환
클라이언트 AuthInterceptor가 자동 reissue → 200 → retry → 다시 401
클라이언트 강제 로그아웃 실행, /login 으로 이동
🔍 원인 분석
실제 JWT 검증을 담당하는 컴포넌트는 Spring MVC AuthInterceptor(HandlerInterceptor)입니다.
그러나 SecurityConfig.filterChain이 .anyRequest().authenticated()로 인증을 요구하는 반면, SecurityFilterChain에는 SecurityContext를 채워주는 JWT 인증 필터가 등록되어 있지 않습니다.
따라서 모든 보호 요청이 SecurityContext가 비어 있는 상태로 .authenticated() 검사에 실패하고, HttpStatusEntryPoint(UNAUTHORIZED)가 직접 401을 반환합니다 (그래서 응답 body가 비어 있음 — ControllerAdvice 미경유).
/api/auth/**만 AUTH_WHITELIST에 포함되어 permitAll 처리되므로, 로그인/리이슈는 통과하지만 그 외 모든 보호 엔드포인트가 차단됩니다.
이는 토큰 발급 문제가 아니라 토큰 검증 자체가 일어나지 않는 구조 결함입니다.
📸 참고 자료
실제 클라이언트 로그 시퀀스:
POST /api/auth/login → 200, accessToken length=146
GET /api/todos → 401 (Response Text: null)
POST /api/auth/reissue → 200, 새 토큰 발급 성공
GET /api/todos (retry, 새 토큰) → 401
🚨 강제 로그아웃 실행
관련 파일:
SS-Web/src/main/java/com/elipair/spacestudyship/config/SecurityConfig.java — 인증 필터 누락 지점
SS-Web/src/main/java/com/elipair/spacestudyship/config/WebConfig.java — AuthInterceptor 등록 위치
SS-Auth/src/main/java/com/elipair/spacestudyship/auth/interceptor/AuthInterceptor.java — 실제 JWT 검증 로직
🗒️ 설명
/api/auth/login은 정상 200, accessToken/refreshToken 발급도 정상이지만, 발급된 토큰으로/api/todos등 인증 필요 엔드포인트 호출 시 401 응답이 반환되며 응답 body가 비어 있습니다.🔄 재현 방법
/home진입/home초기 진입 시 자동 호출되는 GET/api/todos가 401 반환/login으로 이동🔍 원인 분석
AuthInterceptor(HandlerInterceptor)입니다.SecurityConfig.filterChain이.anyRequest().authenticated()로 인증을 요구하는 반면, SecurityFilterChain에는 SecurityContext를 채워주는 JWT 인증 필터가 등록되어 있지 않습니다..authenticated()검사에 실패하고,HttpStatusEntryPoint(UNAUTHORIZED)가 직접 401을 반환합니다 (그래서 응답 body가 비어 있음 — ControllerAdvice 미경유)./api/auth/**만AUTH_WHITELIST에 포함되어permitAll처리되므로, 로그인/리이슈는 통과하지만 그 외 모든 보호 엔드포인트가 차단됩니다.📸 참고 자료
실제 클라이언트 로그 시퀀스:
관련 파일:
SS-Web/src/main/java/com/elipair/spacestudyship/config/SecurityConfig.java— 인증 필터 누락 지점SS-Web/src/main/java/com/elipair/spacestudyship/config/WebConfig.java— AuthInterceptor 등록 위치SS-Auth/src/main/java/com/elipair/spacestudyship/auth/interceptor/AuthInterceptor.java— 실제 JWT 검증 로직SS-Auth/src/main/java/com/elipair/spacestudyship/auth/constant/SecurityUrls.java— 화이트리스트 상수✅ 예상 동작
/api/todos등 보호 엔드포인트가 200으로 응답해야 합니다.{"code":"UNAUTHENTICATED_REQUEST","message":"로그인이 필요합니다."}형식의 JSON body가 반환되어야 합니다.⚙️ 환경 정보
🙋♂️ 담당자