Files
document-haness/docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-refresh-rotation-replica.md
T
DongHyeonkaandClaude Fable 5.1 4d50bb939a docs(keycloak): adopt the decomposition contract, fix the redirect URI, strip evaluative prose
- 계약 채택 — 독자 질문, 후보 29건(PROMOTE 24 · MERGE_INTO 4 · KEEP_IN_SSOT 1). 게시 중
  17건은 전부 유지. 저장소 keycloak-pattern 은 패턴 넷이 브랜치로 갈라져 있어 revisions 로
  tip 넷을 적었다. keycloak-session-store 는 같은 저장소 @ cdac9b8
- 게시된 기록의 redirect_uri 가 SSOT·코드와 달랐다 — OAuth2callback.html → callback.html
  (frontend/src/app.js 에서 확인). 계약 title 이 기록과 다른 7건도 기록 쪽으로 맞췄다
- 미작성 1건 작성 — 패턴 검증을 실제로 돌릴 때의 안전한 순서(Reference)
- 리뷰 100건 반영 — 설명 뒤에 붙은 평가·차례 예고·독자 오해 가정·작성 지시를 지웠다.
  삭제가 남긴 조각 4건을 고치고, 원래부터 잘려 있던 로컬 미리보기 라벨 1건도 닫았다

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

6.6 KiB

id, kind, slug, title, topic, topicName, project, status, version, questionStatus, studio, public, sourceRevision, source
id kind slug title topic topicName project status version questionStatus studio public sourceRevision source
9ae4ec71-a32e-49a7-88c2-f7368541c28d QUESTION refresh-rotation-replica-contention Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가 oauth-oidc-auth-boundary OAuth/OIDC 인증 경계 KeyCloak Patterns 게시 중 33 OPEN https://hyeonworks.com/studio/documents/9ae4ec71-a32e-49a7-88c2-f7368541c28d/edit https://hyeonworks.com/questions/refresh-rotation-replica-contention keycloak-patterns-lab@2026-08
final/document.md#선택의-이유와-지킨-경계-ap1
final/document.md#얻은-것-잃은-것-적용하지-않을-때-ap2

Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가

realm은 refresh token rotation과 재사용 허용 0회를 쓴다. 한 번 갱신하면 이전 refresh token이 바로 무효가 되기 때문에, 두 replica가 같은 refresh token으로 동시에 갱신하면 두 번째 사용이 거부될 수 있다. 실제 Keycloak 응답과 session에 미치는 영향은 아직 재현하지 않았다.

관계

  • BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가 이 질문에 답하기 전에 저장소부터 정해야 한다.
  • Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출 rotation과 재사용 0회를 쓰는 구성이 여기서 나왔다.
  • 서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가 인스턴스를 여러 대 띄워야 이 경쟁이 생긴다.
  • BFF 인증 구조 설계 기준 갱신 실패를 화면 오류로 바꾸는 규칙이 이 기준에 들어 있다.

사실

  • realm에는 refresh token rotation과 재사용 허용 0회가 설정되어 있다.
  • 커밋된 테스트는 새 refresh token이 발급되는지, 이전 token이 거부되는지, revocation 뒤 refresh가 실패하는지를 확인한다. 다만 이 셋은 refresh token을 직접 써서 받은 결과라, 애플리케이션이 스스로 갱신할 때도 같은 결과가 나온다고 보지 않는다.
  • authorized client manager에는 refresh-token provider가 구성되어 있어, access token이 만료되면 refresh를 시도할 수 있다.
  • access token이 만료될 때까지 기다려 실제로 갱신이 성공하고 새 token이 저장되는지까지는 확인하지 않았다.
  • 현재 authorized client 저장소는 프로세스 안에만 있어서(process-local) replica끼리 같은 refresh token 상태를 공유하지 않는다. 그래서 이번 단일 인스턴스 검증에서는 동시 refresh 경쟁을 재현하지 않았다.
  • 이미 발급된 access token은 만료 전까지 API에서 계속 통하기 때문에, 갱신이 실패해도 그동안 화면은 정상으로 보인다.

가정

  • 운영에서는 replica를 둘 이상 띄우고 저장소를 공유하므로, 두 replica가 같은 authorized client 항목을 읽는다.
  • 두 replica의 access token이 비슷한 시각에 만료되면 각 replica가 따로 갱신을 시도한다.

미지수

  • 같은 refresh token으로 두 replica가 동시에 갱신하면 각 replica가 어떻게 동작하는가.
  • 재사용 허용 0회에서 두 번째 사용이 거부되면 사용자 화면에 무엇이 보이는가. 로그인 만료로 보이는가, 일시적 오류로 보이는가.
  • 저장소에서 새 token을 다시 읽어 재시도하면 성공하는가, 아니면 재인증까지 해야 하는가.
  • 갱신을 한 곳에서만 할 것인가, 각자 하게 두고 실패는 재시도로 처리할 것인가.
  • lock을 쓴다면 어디에 두고 얼마나 잡는가. 잡은 채로 프로세스가 내려가면 어떻게 푸는가.
  • 갱신 실패를 로그인 만료와 구분해 표시할 수 있는가.

제약

  • rotation과 재사용 0회는 이미 realm에 설정했다. 이 전제는 바꾸지 않고 답한다.
  • 이미 발급된 access token은 만료 전까지 쓸 수 있으므로 refresh 실패가 곧바로 드러나지 않을 수 있다. 재현 테스트는 access token 만료 직후에 맞춰 실행한다.
  • 이 경쟁은 저장소를 공유한 뒤에야 재현되므로 저장소를 정한 다음에 이어서 푼다.

선택지

1. 분산 lock으로 갱신을 직렬화한다

분산 lock은 여러 프로세스가 같은 자원을 동시에 건드리지 못하도록 프로세스 밖에 두는 잠금이다. 한 replica만 갱신하고 나머지는 그 갱신이 끝나기를 기다렸다가 결과를 읽는다.

대신 lock 저장소의 가용성, lock 만료, 재진입, lock을 쥔 프로세스가 내려간 상황까지 함께 처리해야 한다.

2. 각자 갱신하고 실패는 재시도로 처리한다

구현이 가장 단순하다. 지는 쪽이 거부를 받으면 저장소에서 최신 token을 다시 읽어 재시도한다는 전제인데, 이 재시도가 성립하는지부터 확인해야 한다.

같은 refresh token이 두 번 들어오는 것을 잡아내는 reuse detection 정책에 따라, 두 번째 사용이 token family 전체에 영향을 줄 수 있다. 그러면 재시도 한 번으로 끝나지 않고 재인증이 필요할 수 있다. 갱신에 성공한 replica가 새 token을 저장하기 전에 다른 replica가 저장소를 다시 읽는 순서도 따로 확인해야 한다.

3. 갱신 전용 경로를 하나 둔다

refresh를 전담하는 구성요소 하나만 refresh token을 쓰고, 다른 replica는 그 결과를 조회하게 둘 수 있다.

이 구성요소가 멈추면 access token이 만료된 뒤에 갱신할 주체가 없어진다. 그래서 이 구성요소를 어떻게 살려 두고 어떻게 복구할지를 먼저 정해야 한다.

4. 제약상 제외 — 재사용 허용을 늘린다

재사용을 짧게 허용하면 두 번째 사용이 거부되지 않고 코드도 고칠 필요가 없다. 다만 rotation과 재사용 0회는 이미 realm에 설정했고, 허용한 시간 안에는 훔친 refresh token이 들어와도 막지 못한다.

다음 검증

저장소를 공유한 뒤에 재현한다.

  1. replica 두 대에서 같은 사용자로 access token 만료 직후 동시에 요청을 보낸다.
  2. 이긴 쪽과 지는 쪽의 응답을 각각 기록한다.
  3. 지는 쪽이 저장된 새 token으로 재시도해 성공하는지 본다.
  4. 지는 쪽 사용자 화면에 무엇이 보이는지 기록한다.
  5. lock을 넣은 구성과 안 넣은 구성을 같은 입력으로 비교해 실패율과 지연을 잰다.

실패가 사용자에게 노출되면 lock을 고르고, 노출되지 않으면 재시도로 둔다. rotation과 재사용 0회를 바꾸는 선택지는 지금은 제외로 두고 나중에 다시 본다.