30 KiB
title, source_type, status, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, branch, parent_branch, related_projects, tags, created, target_merge, status_label, contract_packet_sha256
| title | source_type | status | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | branch | parent_branch | related_projects | tags | created | target_merge | status_label | contract_packet_sha256 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-keycloak-refresh-token-rotation (Refresh token rotation + revocation) | branch-note | raw | BR-KEYCLOAK-CHILD-579E54CC | branch-child | keycloak-patterns-overview | WI-KEYCLOAK-PATTERNS-OVERVIEW-007 |
|
|
1 | feature-keycloak-refresh-token-rotation | feature-keycloak-refresh-rotation-and-logout |
|
|
2026-05-25 | in-progress | 41b3c869ae7eecc249938a19e9501c8b8cecdb11d589882f2babb6d10f63618a |
branch: feature-keycloak-refresh-token-rotation — Refresh token rotation + revocation
Layer:
raw/branch-notes/— raw/branch-notes/feature-keycloak-refresh-rotation-and-logout Work Item branch의 child. 목적: refresh token rotation의 개념·정책·검증 계약 owner로서, 공식 확인된Revoke Refresh Token동작과 target-version 실험이 필요한Refresh Token Max Reuse/reuse 결과를 구분한다./revoke계약과 JWT stateless 한계도 함께 정리한다.status_label:in-progress|review|merged|abandoned
부모 (필수)
raw/branch-notes/feature-keycloak-refresh-rotation-and-logout
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: refresh rotation과 logout 후 session·token 무효화가 검증된다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1 |
AP1은 public client와 Authorization Code + PKCE를 사용한다 | public SPA의 refresh token rotation·revocation 계약에 적용한다 | raw/project-notes/keycloak-patterns-overview |
DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1 |
done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | parent의 rotation·logout 검증을 위한 개념·실험 계약에 적용한다 | raw/project-notes/keycloak-patterns-overview |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
없음.
목표
P2A는 SPA가 refresh_token을 보유하므로 탈취 시 공격자가 access_token을 계속 갱신할 수 있다. 공식 자료로 확인된 rotation 계약은 사용된 refresh token을 무효화하고 새 refresh token을 발급한다는 범위까지다(KC-RTROT-C1/C2). 이미 사용한 토큰을 다시 제출했을 때 후속 토큰까지 무효화되는지, 그 범위가 token family 전체인지, Max Reuse 값별 의미가 무엇인지는 target Keycloak 버전의 실행 실험으로만 확정한다.
핵심 질문:
- Keycloak에서 rotation을 켜는 정확한 설정 항목과 위치는?
- 이미 사용한 refresh token을 다시 제출하면 어떤 토큰·세션이 무효화되는가? (
Max Reuse=0과1비교 관찰) /protocol/openid-connect/revokeendpoint 사용 방법?- logout 시 access_token / refresh_token / session을 어떻게 정리?
- 함정: JWT access_token은 stateless — revoke를 호출해도 만료까지 검증을 통과한다. 즉시성 확보 방법은?
본 sub-sub-branch는 Keycloak Realm Settings 경로 + rotation flow + revocation endpoint + logout 정리 + stateless 한계를 정리.
- 이슈: (학습 노트, 이슈 없음)
- PR: (구현 없음)
범위
포함 범위
본 sub-sub-branch 는 P2A 개념·계약 정리(
documented-only). "무엇이 어떻게 동작하는가 + 어떤 설정/파라미터 계약인가" 까지만 다루고, 실제 docker-compose 시연·실측은 cousin(다른 phase) raw/branch-notes/feature-keycloak-refresh-rotation-and-logout (P3A) 에 위임한다.
- Keycloak refresh token rotation 설정의 개념·계약:
Revoke Refresh Token토글 동작 + rotation flow(RT 1회 사용 후 무효화·새 토큰 발급) —KC-RTROT-C1/C2 - reuse detection 결과에 대한 가설·검증 계약 — 후속 RT 유효성, 세션 상태,
Max Reuse=0/1차이를 P3A 실행 문서에서 관찰하며 family invalidation을 선결 사실로 두지 않음 /protocol/openid-connect/revokeendpoint 의 RFC 7009 request/response 계약 (token/token_type_hint, refresh↔access 무효화 SHOULD) —RFC7009-C1~C4- JWT stateless access token 의 revoke 즉시성 한계 + 대응 옵션(짧은 TTL / introspection / blacklist / opaque) 트레이드오프 —
RFC7009-C5~C7 - RP-Initiated(front-channel) logout 파라미터 계약(
id_token_hint,post_logout_redirect_uri, Valid Post Logout Redirect URIs) —KC-LOGOUT-C1~C7 - 짧은 access token TTL 로 revoke 즉시성을 완화하는 설계 근거(RFC 자신의 short-lived-token 대안) —
RFC7009-C6
제외 범위
의도적으로 제외한 것. 면접 등에서 "이건 범위에 없었습니다"라고 답할 근거.
- 실제 rotation/revoke/logout 의 docker-compose 시연·실측 — cousin(P3A) raw/branch-notes/feature-keycloak-refresh-rotation-and-logout 소유
- backend 매 요청 introspection 호출(stateless 포기 패턴) — 한계만 인지, 채택 안 함
- distributed token blacklist 캐시(Redis 등) 운영 패턴
- Keycloak custom SPI / event listener
- back-channel logout 수신 backend 구현과 provider-trigger E2E — 현재 두 refresh note 모두 범위 밖. 필요 시 공식 spec·framework 근거를 갖춘 전용 branch를 새로 만들어야 하며 현재 endpoint 존재를 가정하지 않음
- opaque / reference token 으로의 전환(Keycloak 지원하나 본 학습 범위 외)
근거 (필수, 최소 1개+)
- raw/official-docs/keycloak-securing-apps-overview-official — Keycloak Securing Apps overview (Tokens / revocation)
- raw/official-docs/oauth-v2-1-draft-ietf — OAuth 2.1 (refresh_token rotation 권고)
- raw/official-docs/oauth2-pkce-rfc-7636 — PKCE (refresh_token 보안 맥락)
- raw/official-docs/keycloak-refresh-token-rotation-sessions-official — Keycloak Server Administration Guide (§_timeouts + §_refresh_token_rotation). D1(
Revoke Refresh Token토글 존재·동작)과 D4(RT 1회 사용 후 invalidate) 를 부분 뒷받침.Refresh Token Max Reuse필드와 "family invalidate" 메커니즘은 이 자료에서 확인되지 않음(KC-RTROT-C6) — D1/D4 의UNSUPPORTED_DECISION라벨은 유지 필요. - raw/official-docs/oauth2-token-revocation-rfc-7009 — RFC 7009 Token Revocation (revoke endpoint 표준 request/response 계약 + self-contained/JWT access token 의 revoke 즉시성 한계의 표준 근거,
RFC7009-C1~C6) - raw/official-docs/keycloak-refresh-token-rotation-reuse-admin-official — Keycloak Server Admin Guide "Tokens tab" — "Revoke Refresh Token" 설정 공식 정의 확보 + "Refresh Token Max Reuse"/family-invalidate 동작의 verbatim 부재를 전수 검색으로 확인(negative finding). D1/D4는 여전히
UNSUPPORTED_DECISION유지. ⚠️ 위keycloak-refresh-token-rotation-sessions-official와 동일 소스(server_admin Tokens 탭)의 중복 발췌 — 병합/아카이브는 사용자 판단(§보고 참조) - raw/official-docs/keycloak-oidc-logout-endpoint-official — Keycloak RP-Initiated(front-channel) logout 파라미터 계약(
end_session_endpoint,id_token_hint,post_logout_redirect_uri, Backchannel Logout URL). D3 + 구현 가이드 §3 근거 (KC-LOGOUT-C1~C7) - raw/official-docs/oauth2-browser-based-apps-ietf-draft — OAuth 2.0 for Browser-Based Apps (IETF BCP). browser-based OAuth client = public client 가 토큰을 브라우저에 보유 → 탈취 위협 배경(D1
선택 조건,OAUTH-BBA-C3)
TODO
각 항목 옆에 증거 등급 표기: actually-implemented | locally-verified | prod-verified | documented-only | planned | needs-confirmation
- Keycloak Realm Settings → Tokens 탭 항목 — 등급:
documented-onlyRevoke Refresh Token: ON — refresh 사용 후 해당 토큰 무효화Refresh Token Max Reuse: target UI에서 필드 존재를 확인한 뒤 0과 1을 실험 입력값으로 비교. 사전에 각 값의 의미를 부여하지 않음SSO Session Idle: 짧게 (예: 30분) — 일정 시간 미사용 시 세션 만료SSO Session Max: 강제 만료 시간 (예: 10h)Access Token Lifespan: 5~15분 (짧을수록 revoke 즉시성 향상)Client Session Idle/Client Session Max: client별 override
- Refresh Token Rotation flow — 등급:
documented-only1) SPA가 RT_1으로 /token (grant_type=refresh_token) 호출 2) Keycloak: RT_1 검증 → invalidate → AT_2 + RT_2 반환 3) SPA가 RT_2로 다음 갱신 → RT_2 invalidate → AT_3 + RT_3 - Reuse Detection 결과 가설 검증 — 등급:
needs-confirmation- 공격자가 RT_1을 탈취하고 사용 → AT_2 + RT_2 받음
- 정상 사용자가 (모르고) RT_1을 다시 사용 → RT_1 응답과 RT_2의 후속 사용 결과, realm session 상태를 각각 관찰
Max Reuse=0과1에서 같은 sequence를 실행해 후속 토큰 무효화 범위를 기록. family 전체 invalidation은 가능한 관찰 결과 중 하나일 뿐 기대값으로 고정하지 않음
- Revoke endpoint 사용법 — 등급:
documented-onlyPOST /realms/<realm>/protocol/openid-connect/revoke token=<token> token_type_hint=refresh_token (또는 access_token) client_id=<spa-client>token/token_type_hint파라미터 계약은 RFC 7009 §2.1 표준과 일치 — raw/official-docs/oauth2-token-revocation-rfc-7009RFC7009-C3(Keycloak 이 이 endpoint 를 실제로 RFC 7009 로 문서화하는지는RFC7009-C1의 "Does not prove" 참조 — 별도 vendor 확인 필요)- refresh_token revoke: RFC 상 SHOULD 로 관련 access token 도 함께 무효화될 수 있음(
RFC7009-C4) — MUST 아님, AS 지원 여부에 달림 - access_token revoke: Keycloak은 introspection 시 invalid 응답, 그러나 JWT를 stateless로 검증하는 backend는 모름 (
RFC7009-C5/C7, 아래 함정 참조)
- Logout 시 토큰 정리 — 등급:
documented-only- (a) Front-channel logout:
/protocol/openid-connect/logout?post_logout_redirect_uri=...&id_token_hint=<id_token>— 브라우저 redirect로 Keycloak 세션 종료 - (b) Back-channel logout: Keycloak client 설정 필드의 존재만 기록. 수신 endpoint와 provider-trigger E2E는 현재 범위에 없고 구현을 가정하지 않음
- (c) Refresh token revoke: 명시적으로
/revoke호출 - SPA가 메모리에서 토큰 삭제 + cookie clear도 추가
- (a) Front-channel logout:
- 함정: JWT access_token stateless 한계 — 등급:
documented-only- JWT는 자체 서명 검증으로 valid 여부 판단 → backend가 revoke 사실을 모름 — raw/official-docs/oauth2-token-revocation-rfc-7009
RFC7009-C5(self-contained access token 은 AS 와 추가 상호작용 없이 인가 판단)가 표준 근거 - access_token 만료(
exp)까지 backend는 valid로 통과시킴 —RFC7009-C5(self-contained token 은 AS 상호작용 없이 검증) +RFC7009-C4(access token 무효화는 AS 가 지원할 때만 SHOULD, MUST 아님) - 대응 옵션:
- 짧은 TTL (5~15분) — 가장 일반적
- Token Introspection (
/protocol/openid-connect/token/introspect) — 매 요청마다 Keycloak에 질의 → stateless 이점 상실, 성능 저하 - Revocation list / blacklist — backend가 revoked jti 목록 캐싱 (운영 복잡)
- Reference token (opaque) — JWT 대신 opaque token + introspection (Keycloak 지원하나 본 학습 범위 외)
- JWT는 자체 서명 검증으로 valid 여부 판단 → backend가 revoke 사실을 모름 — raw/official-docs/oauth2-token-revocation-rfc-7009
- 함정 정리 — 등급:
documented-onlyRefresh Token Max Reuse의 0/양수 의미를 실험 없이 일반화하면 버전별 동작을 잘못 문서화할 수 있음- logout 시
id_token_hint누락하면 prompt 떠서 UX 저하 - rotation 활성화 후 SPA 코드가 옛 RT를 재사용하면 실패하거나 후속 RT/세션에 영향이 갈 수 있음 → 정확한 범위는 실행 결과로 기록
- back-channel receiver가 없는 현재 scope에서 backend cache 무효화를 보장한다고 쓰지 않음
진행 중 메모
작업하며 떠오른 메모. 자유 형식.
- Keycloak 25.x 기준 Realm Settings → Tokens 탭 UI 항목은 버전에 따라 라벨이 약간 달라질 수 있음. 실 구현(P3A) 시 정확한 라벨 재확인 필요.
- "JWT는 revoke가 안 된다"는 표현은 정확히는 "Keycloak이 revoke를 알리지만 stateless backend가 그 사실을 가져오지 않으면 모른다"가 맞음. 짧은 TTL + rotation 조합으로 실용적 보안 확보.
- Keycloak의
Backchannel Logout URL설정 필드 존재와 실제 수신 구현은 별개다. 현재 문서들은 receiver endpoint를 구현·위임하지 않으며, 필요 시 전용 branch에서 spec/framework 지원부터 확인한다.
결정 사항 (decisions)
추후 면접/회고에서 "왜 이렇게 했나" 답할 근거. 대안과 함께 기록.
- 2026-05-25 (수정 2026-07-18):
Revoke Refresh Token: ON은 rotation 실험의 candidate profile로 유지한다.Refresh Token Max Reuse: 0은 1과 비교할 실험 입력값이며, 다른 값이 탐지를 약화시킨다는 의미는 target-version 결과 전에는 주장하지 않는다. - 2026-05-25: access_token revocation 즉시성은 **짧은 TTL(5~15분)**로 해결. introspection은 stateless 이점 상실 + 성능 저하로 학습 범위에서 권장 안 함.
- 2026-07-18: back-channel logout receiver와 provider-trigger E2E는 P2A/P3A 두 refresh note 모두 범위 밖이다. 현재 cousin에 위임하지 않으며, 필요 시 전용 branch를 신설한다.
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. 본 sub-sub-branch 는
documented-only.Revoke Refresh Token토글의 동작은 이제 Keycloak 공식 doc 로 뒷받침되나(KC-RTROT-C1/C2),Refresh Token Max Reuse필드명과 "재사용 시 family 전체 invalidate" 동작은 Keycloak 26.7.0 Server Admin Guide 전수 검색에서 verbatim 부재 확인(KC-RTROT-C6) → 해당 부분만UNSUPPORTED_DECISION유지, 실측은 P3A cousin 에 위임.선택 조건열(R2): "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면N/A.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | rotation 정책·검증 profile owner — Revoke Refresh Token: ON을 candidate로 두고 Refresh Token Max Reuse=0/1을 비교 실험한다. 최종 값과 의미는 target-version 관찰 뒤 확정 |
SPA(public client)가 refresh token을 브라우저에 보유하는 P2A/P3A 배치에서 rotation을 평가. BFF/token-mediating backend(raw/branch-notes/feature-keycloak-bff-vs-spa-direct)에서는 위협 모델이 달라 재평가. 병렬 refresh가 필요한 client는 suppress-refresh-token-rotation executor 예외(KC-RTROT-C3) 검토 |
raw/official-docs/keycloak-refresh-token-rotation-sessions-official.md#KC-RTROT-C1, #KC-RTROT-C2, raw/official-docs/oauth-v2-1-draft-ietf.md#OA21-C3, raw/official-docs/oauth2-browser-based-apps-ietf-draft.md#OAUTH-BBA-C3. Max Reuse 필드·값 의미·family 동작은 UNSUPPORTED_DECISION(KC-RTROT-C6) |
official-vendor-doc + official-standard + needs-confirmation (Max Reuse semantics) |
target UI/export에서 필드와 내부 key를 확인하고 0/1에서 동일 reuse sequence를 실행. family invalidation 여부는 결과값으로만 기록 |
| D2 | access token revocation 즉시성은 짧은 TTL(5~15분) 로 해결, introspection 패턴은 stateless 이점 상실로 권장 안 함 | stateless JWT 검증(Spring RS)을 유지하는 한 → 짧은 TTL. "초 단위 즉시 무효화"가 hard requirement 면 → introspection 또는 opaque/reference token(stateless 포기 + 성능 비용) | raw/official-docs/oauth2-token-revocation-rfc-7009.md#RFC7009-C5 (self-contained/JWT access token 은 AS 와 추가 상호작용 없이 검증 → revoke 즉시 반영 안 될 수 있음) + #RFC7009-C6 (짧은 수명 access token 이 RFC 자신의 설계 대안) + #RFC7009-C4 (access token 무효화는 AS 가 access token revocation 을 지원할 때만 SHOULD — 미지원/self-contained 시 즉시 무효화 안 됨). 방향성은 official-standard 근거 보유. 구체적 수치 "5~15분" 은 RFC 가 분 단위를 제시 안 하므로 UNSUPPORTED_IMPL_DECISION (trade-off: 짧을수록 안전하나 refresh 왕복/서버 부하↑ — 5~15분은 임의 균형점) |
official-standard (방향성) + UNSUPPORTED_IMPL_DECISION (TTL 수치) |
Keycloak 이 access token revocation(RFC7009 §2 SHOULD)을 실제 지원하는지 확인 + OWASP/Keycloak 공식 권장 TTL 구간 raw 추가로 수치 보강 |
| D3 | RP-Initiated logout 파라미터 계약까지만 소유. back-channel receiver 구현·provider-trigger E2E는 현재 scope 밖이며 endpoint를 가정하지 않음 | 현재 요구는 브라우저 logout과 revoke 계약 학습. backend cache 즉시 무효화가 별도 요구가 되면 전용 branch에서 OIDC Back-Channel Logout spec과 framework 지원을 확보한 뒤 설계 | raw/official-docs/keycloak-oidc-logout-endpoint-official.md#KC-LOGOUT-C7 (설정 필드 존재만 증명) + UNSUPPORTED_DECISION (receiver 미설계) |
official-vendor-doc (field only) + scoped out |
receiver가 구현됐다는 인상을 주는 링크·예상 endpoint를 두지 않음 |
| D4 | reuse 결과 검증 계약 — RT_1 사용 후 무효화·AT_2+RT_2 발급까지는 공식 계약, RT_1 재사용 응답과 RT_2/realm session 상태는 관찰 항목 | D1 profile의 0/1 각각에 동일 sequence 적용. family 전체 invalidation은 가능한 결과 중 하나이며 expected fact가 아님 | raw/official-docs/keycloak-refresh-token-rotation-sessions-official.md#KC-RTROT-C2 + raw/official-docs/oauth-v2-1-draft-ietf.md#OA21-C3 + UNSUPPORTED_DECISION(KC-RTROT-C6) |
official-vendor-doc (초기 rotation) + needs-confirmation (reuse impact) |
P3A 실행 owner가 RT_1 재사용 status, RT_2 후속 status, session 상태를 분리 기록해야 함 |
구현 가이드
결정 (Decisions) 이 "무엇 을 할 것인가" 라면, 본 §는 "어디에 어떻게 구현될 것인가" 의 사전 명세 — 다음 구현자(P3A cousin)가 되묻지 않아도 설정·호출을 작성할 수 있는 수준. 본 노트는
documented-only이므로 각 항목은 설정/파라미터 계약까지이며, 실측 승격은 Claims To Verify + P3A cousin 소관.3-rule: (R1) 각 cell 은 Decision ID + Supporting Claim ID trace, (R2) 근거 없는 detail 은
UNSUPPORTED_IMPL_DECISION+ trade-off 한 줄, (R3) 본 branch 결정 범위 밖은 남기지 않음.
1. Keycloak Realm Settings — rotation & timeout 설정 계약
Trace: D1(
KC-RTROT-C1/C2) + D2(KC-RTROT-C4) — Realm Settings → Sessions/Tokens 탭.
- UNSUPPORTED_IMPL_DECISION:
Refresh Token Max Reuse필드명·0/1 의미·reuse 후 영향(KC-RTROT-C6doc text 부재 — admin UI/export와 실험 필요) /Access Token Lifespan의 "5~15분" 수치(RFC·Keycloak doc 모두 분 단위 미제시).
| 설정 | 위치 (Realm Settings) | 값 | 근거 / 상태 |
|---|---|---|---|
Revoke Refresh Token |
Sessions/Tokens 탭 | Enabled | KC-RTROT-C1 — documented |
Refresh Token Max Reuse |
Tokens 탭 (노출 여부 포함 확인) | 0과 1을 각각 실험 | UNSUPPORTED_IMPL_DECISION — 필드·값 의미 doc text 부재(KC-RTROT-C6), UI/export + runtime 비교 |
Access Token Lifespan |
Tokens 탭 | 5~15분 | KC-RTROT-C4(설정 존재) + UNSUPPORTED_IMPL_DECISION(수치) |
SSO Session Idle / SSO Session Max |
Sessions 탭 | 프로젝트값(예: 30m / 10h) | KC-RTROT-C4 — documented |
Client Session Idle / Client Session Max |
Sessions 탭 | SSO 값보다 짧게(client override) | KC-RTROT-C4 — documented |
2. Revoke endpoint 호출 계약 (RFC 7009)
Trace: D2 +
RFC7009-C1~C4.
- UNSUPPORTED_IMPL_DECISION: Keycloak 이 이 endpoint 를 RFC 7009 로 문서화/준수한다는 명시는 vendor 확인 필요(
RFC7009-C1"Does not prove"). 여기서는 RFC 표준 계약 형태만 고정.
POST /realms/<realm>/protocol/openid-connect/revoke
token=<token> # REQUIRED (RFC7009-C2)
token_type_hint=refresh_token|access_token # OPTIONAL, 서버 조회 최적화용 (RFC7009-C3)
client_id=<spa-client> # client 인증
- refresh_token revoke → AS 가 access token revocation 을 지원하면 관련 access token 도 SHOULD 함께 무효화(
RFC7009-C4— MUST 아님). - access_token revoke → Keycloak introspection 은 invalid 로 응답하나, JWT 를 stateless 로 검증하는 backend 는 그 사실을 모름(§4 참조).
3. Front-channel(RP-Initiated) logout 파라미터 계약
Trace:
KC-LOGOUT-C1~C7(In-scope logout; back-channel 수신 은 D3 로 out of scope).
| 요소 | 계약 | 근거 |
|---|---|---|
| endpoint | /realms/<realm>/protocol/openid-connect/logout (= end_session_endpoint) |
KC-LOGOUT-C1/C2 |
id_token_hint |
없으면 로그아웃 confirm UI 가 뜰 수 있음 → UX 위해 전달 권장 | KC-LOGOUT-C3 |
post_logout_redirect_uri |
제공 시 자동 redirect. 단 client_id 또는 id_token_hint 동반 필수 + client 의 Valid Post Logout Redirect URIs 와 매칭 필요 |
KC-LOGOUT-C4/C5/C6 |
Backchannel Logout URL (client 설정) |
필드 정의만 in-scope. receiver endpoint와 provider-trigger E2E는 현재 존재를 가정하지 않으며 별도 요구 시 전용 branch 필요 | KC-LOGOUT-C7 |
4. Stateless JWT access token 즉시성 완화 config
Trace: D2 +
RFC7009-C5/C6/C7.
- UNSUPPORTED_IMPL_DECISION: TTL 수치(§1 과 동일 trade-off).
- backend(Spring RS)는 서명 +
iss/aud/exp만 검증 → revoke 사실을 모름(RFC7009-C5).exp만료까지 valid 통과(RFC7009-C5self-contained +RFC7009-C4조건부 SHOULD). - 짧은 TTL = RFC 자신이 제시하는 설계 대안(
RFC7009-C6). 채택.
| 대응 옵션 | stateless 유지? | 비용 | 본 노트 판정 |
|---|---|---|---|
| 짧은 TTL (5~15분) | ✅ 유지 | revoke 후 최대 TTL 만큼 노출 창 | 채택 (RFC7009-C6) |
| Token Introspection (매 요청) | ❌ 포기 | 매 요청 Keycloak 왕복·성능↓ | 한계만 인지, 미채택 |
| Revocation list / jti blacklist | 부분 | backend 캐시 운영 복잡 | out of scope |
| Opaque/reference token | ❌ 포기 | introspection 상시 | out of scope |
엣지·실패·의존
R4 캡처용. 정상 경로 외에 구현 중 부딪힐 실패/엣지/다른 계약 의존을 미리 열거.
- 실패·엣지 경로:
- RT 캐시 후 옛 RT 재사용 → RT_1 요청이 실패하고 후속 RT/세션에도 영향이 갈 수 있음. 정확한 범위는 D4 실험으로 관찰. 클라이언트는 in-flight refresh를 단일 진입점으로 직렬화한다.
- 동시 silent renew race → 동일 RT 동시 제출은 D4 관찰을 오염시킬 수 있음. 0/1 각 실험에서 단일 refresh 진입점을 보장하고 manual 시연 시 silent renew를 일시 중단한다.
- logout
id_token_hint누락 → confirm prompt 로 UX 저하(KC-LOGOUT-C3). 기대: id_token 보관 후 전달. post_logout_redirect_uri미등록 →Valid Post Logout Redirect URIs매칭 실패로 redirect 거부(KC-LOGOUT-C6). 기대: client 설정에 사전 등록.- access_token revoke 직후 만료 전 호출 → 200 통과(stateless JWT,
RFC7009-C5) — 함정(의도된 한계). 기대:exp까지 유효, TTL 후 401. - back-channel receiver 부재 → 현재 범위에서는 backend session/cache의 즉시 무효화를 보장하지 않는다. 필요 시 전용 branch를 생성한다.
- 다른 계약 의존:
- 부모 raw/branch-notes/feature-keycloak-internal-spa-direct-no-google — P2A 배치(SPA-direct, no Google) 컨텍스트를 consume. 배치가 BFF 로 바뀌면 D1 전제(브라우저가 RT 보유) 붕괴.
- raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff
D1/D4— RT 를 브라우저 어디에 저장하느냐(그 브랜치 D1: AT 메모리 + RT httpOnly cookie)가 탈취 위험/rotation 필요성의 전제이며, 그 브랜치 D4 가 "refresh_token 은 rotation 에 의존" 을 명시. 저장 결정이 바뀌면 본 브랜치 위협모델 영향. - raw/branch-notes/feature-keycloak-spring-rs-audience-validator
D1— backend 가iss+signature+exp+aud4종 검증(그 브랜치 D1)이 D2 stateless 한계의 전제.exp만료 시 401 동작이 §4 의 근거. - raw/branch-notes/feature-keycloak-pkce-flow-stages — 최초 토큰(AT_1/RT_1) 발급 흐름(PKCE)을 consume — rotation 은 그 이후 단계.
- cousin(다른 phase) raw/branch-notes/feature-keycloak-refresh-rotation-and-logout (P3A) — 본 개념 계약의 실측·시연 소유. 본 노트 = 개념/계약, 그쪽 = 실행.
검증해야 할 주장
공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
Keycloak Realm Settings → Tokens 탭에 Revoke Refresh Token 토글과 Refresh Token Max Reuse 입력이 정확히 그 라벨로 존재 |
Keycloak 버전마다 admin UI 라벨이 달라질 수 있음; cited raw 에서 verbatim 미회수 | Keycloak 25.x docker 컨테이너 실행 후 admin UI 캡처 + Server Admin Guide raw source 발췌 추가 | needs-confirmation |
Refresh Token Max Reuse=0과 1에서 RT 재사용 결과가 어떻게 다른지(후속 RT·realm session 포함) |
필드·값 의미와 family invalidation 메커니즘이 cited raw에 verbatim 없음 | 각 값으로 realm을 재설정한 뒤 RT_1 사용→RT_2 발급→RT_1 재사용→RT_2 후속 사용→session 상태를 동일 순서로 기록 | needs-confirmation |
/protocol/openid-connect/revoke 엔드포인트가 RFC 7009 Token Revocation 을 준수한다 |
RFC 7009 의 raw source 부재; Keycloak 의 RFC 준수 여부 verbatim 인용 없음 | RFC 7009 raw 발췌 후 Keycloak Server Admin Guide 의 "Token Revocation" 섹션과 cross-check | needs-confirmation |
| JWT stateless backend 가 access_token revoke 사실을 모름 — 만료 전 검증 통과 | OAuth 2.1 / PKCE RFC 에 stateless JWT introspection 트레이드오프의 verbatim 인용 없음 | Spring Security Resource Server 로 JWT 검증 설정 후, revoke 직후 동일 token 으로 호출 → 200 응답 확인 (TTL 5분) | planned |
| RP-Initiated logout 파라미터가 target Keycloak에서 문서 계약대로 동작하는지 | 공식 파라미터 계약은 있으나 runtime 미검증. back-channel receiver는 본 claim과 scope에 포함하지 않음 | front-channel logout만 실행해 session 종료·redirect를 확인. back-channel 요구가 생기면 전용 branch에서 별도 검증 | needs-confirmation |
마주친 문제
- 이슈 1: rotation 상태에서 SPA가 옛 refresh_token을 재시도하면 정상 사용자 흐름도 실패할 수 있음.
- 원인 가설: reuse 처리 범위가 정상/공격 주체를 구분하지 않을 수 있음. 후속 RT·session 영향은 target-version 실험 전 확정하지 않음
- 시도: (구현 없음)
- 해결: SPA가 refresh 진행 중에는 단일 진입점으로 직렬화 (in-flight refresh promise 공유) —
documented-only
묶음
- raw/official-docs/keycloak-refresh-token-rotation-reuse-admin-official
- raw/official-docs/keycloak-refresh-token-rotation-sessions-official
- raw/official-docs/oauth-v2-1-draft-ietf
- raw/official-docs/oauth2-token-revocation-rfc-7009
- raw/official-docs/oidc-client-ts-library
- raw/official-docs/owasp-html5-storage-xss-spa
본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
오류 기록 (이 sub-sub-branch 작업 중 발생)
- (없음 — 현재 documented-only 단계)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase 3 실 구현 단계에 누적)
관련 일일 노트
완료 후 정리
머지/종료 시점에 채움.
/ingest가 이 섹션을 기준으로 wiki/projects/에 추출.
- PR 링크: (미구현 — 문서까지만)
- 리뷰 메모:
- 머지 결과 / 배포 환경: 없음 (P2A는
documented-only범위) - wiki 추출 대상: 현 단계 없음. 추후
wiki/concepts/refresh-token-rotation-revocation.md로 합성 후보 (다른 패턴과 공통). - 추출하지 않을 항목: P2A 구현 없음.
documented-only유지.