Skip to content

fix(auth): 회전 직후 직전 refresh 토큰 재제출은 세션을 끊지 않는다 (SEC-05) - #371

Merged
selee1012 merged 1 commit into
devfrom
fix/refresh-rotation-grace
Oct 29, 2026
Merged

selee1012 merged 1 commit into
devfrom
fix/refresh-rotation-grace

Conversation

@selee1012

Copy link
Copy Markdown
Contributor

왜

prod SEC-05(refresh 재사용 감지) 3건(10/28 17:09, 10/29 09:23·11:08 KST)이 모두 같은 모양이었습니다.

  1. 앱을 열면 만료된 AT 로 보낸 요청 여러 개가 401 을 받음
  2. refresh 가 한 번 성공(RT1 → RT2)
  3. 3~8초 뒤 직전 RT1 로 refresh 가 한 번 더 옴
  4. 서버가 탈취로 보고 family 전체를 폐기 → 사용자 강제 로그아웃

탈취가 아니라 앱의 refresh 경합입니다. 근본 수정은 앱의 single-flight(Android 이슈로 따로 남김)이고, 이 PR 은 서버 쪽 안전망입니다.

무엇을

AuthService.refresh 에 회전 유예 30초를 둡니다. 아래를 모두 만족하면 재사용으로 보지 않고 같은 family 에 새 페어를 하나 더 줍니다.

  • 회전으로 폐기된 지 30초 이내
  • 재사용 감지 이력이 없고 만료되지 않음
  • 그 회전이 만든 후속 토큰(parent_token_id)이 있고 모두 살아 있음

그래서 다음은 기존과 똑같이 재사용 감지(401 + family 폐기 + SEC-05)입니다.

  • 30초가 지난 재제출
  • 후속 토큰이 다시 회전된 뒤의 재제출
  • 로그아웃·새 기기 로그인·정지·탈퇴 뒤의 재제출 — 이 경로들은 후속 토큰까지 폐기하므로 유예 조건에 걸리지 않음

유예는 SEC-05 로 세지 않고 refresh_rotation_grace userId=… sinceRotationMs=… INFO 로그로 남깁니다.

바뀌지 않는 것

  • 스키마 변경 없음(마이그레이션 없음). parent_token_id 인덱스는 이미 있음
  • API 계약 동일(응답 형식 그대로). Swagger 설명에 유예 동작만 덧붙임
  • 행 잠금(findByTokenHashForUpdate)은 그대로 — 동시 요청이 직렬화된 뒤 두 번째가 유예로 처리됨

테스트

SessionDeviceFlowIT

  • 동시 refresh 둘 다 200, 두 새 RT 모두 사용 가능
  • 회전 직후 직전 RT 재제출 → 200, 재사용 기록 없음
  • 31초 지난 재제출 → 401, 재사용 기록, 후속 RT 폐기
  • 후속이 다시 회전된 뒤 재제출 → family 폐기
  • 로그아웃 뒤 직전 RT 재제출 → 401 (로그아웃이 되살아나지 않음)

로컬: auth.*·security.*·observability.*·docs.* 통과.

🤖 Generated with Claude Code

prod SEC-05 3건(10/28 17:09, 10/29 09:23·11:08 KST)이 모두 같은 모양이었다. 앱을 열면 만료된 AT 로
요청 여러 개가 401 을 받고, refresh 가 한 번 성공한 뒤 3~8초 뒤에 직전 RT 로 refresh 가 한 번 더 왔다.
서버는 이를 탈취로 보고 family 전체를 폐기해 사용자를 로그아웃시켰다.

회전으로 폐기된 지 30초 안이고 그 회전이 만든 후속 토큰이 모두 살아 있으면 같은 family 에 새 페어를 하나 더 준다.
로그아웃·새 기기 로그인·정지·탈퇴(후속까지 폐기), 후속이 다시 회전된 경우, 30초가 지난 경우는 기존대로 재사용 감지다.
유예는 SEC-05 로 세지 않고 refresh_rotation_grace 로그로 남긴다. 앱 single-flight 는 Android 이슈로 따로 둔다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Test Coverage

Overall Project 83.61% -0.01% 🍏
Files changed 91.57% 🍏

File Coverage
AuthService.java 78.78% -0.45% 🍏
AuthController.java 71.43% 🍏

@selee1012
selee1012 merged commit 8ee2c50 into dev Oct 29, 2026
3 checks passed
@selee1012
selee1012 deleted the fix/refresh-rotation-grace branch October 29, 2026 03:35
selee1012 added a commit that referenced this pull request Oct 29, 2026
release: SEC-05 refresh 회전 유예 (#371)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant