--- title: branch / feature-keycloak-four-pattern-tradeoff-matrix source_type: branch-note status: raw id: BR-KEYCLOAK-PATTERNS-OVERVIEW-019 kind: project-work-item project: keycloak-patterns-overview work_item: WI-KEYCLOAK-PATTERNS-OVERVIEW-019 inherits: [DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1, DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1] refines: [] overrides: [] depends_on: [WI-KEYCLOAK-PATTERNS-OVERVIEW-003, WI-KEYCLOAK-PATTERNS-OVERVIEW-008, WI-KEYCLOAK-PATTERNS-OVERVIEW-010, WI-KEYCLOAK-PATTERNS-OVERVIEW-012] imports: [] delegates: [] accepts_delegations: [] contract_packet: 1 contract_packet_sha256: d06cbb2df49a1cb669394e4f97e96c796f4c2fdfed0d7fd63021b63af73e8234 branch: feature-keycloak-four-pattern-tradeoff-matrix parent_branch: related_projects: [keycloak-patterns-overview] tags: [branch] created: 2026-07-23 target_merge: status_label: in-progress --- # branch: feature-keycloak-four-pattern-tradeoff-matrix ## 부모 (필수) - [[raw/project-notes/keycloak-patterns-overview]] ## 브랜치 계약 패킷 - **생성 시 프로젝트 개정**: `1` - **패킷 스키마**: `contract_packet: 1` - **완료 조건**: 4패턴 비교표의 모든 cell이 구현 WI evidence를 가리킨다 ### 상속한 프로젝트 결정 | Decision Ref | Project Summary | Branch Application | Source | |---|---|---|---| | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `WI-KEYCLOAK-PATTERNS-OVERVIEW-019` 완료 조건에 적용 | `[[raw/project-notes/keycloak-patterns-overview]]` | | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `WI-KEYCLOAK-PATTERNS-OVERVIEW-019` 완료 조건에 적용 | `[[raw/project-notes/keycloak-patterns-overview]]` | ### 브랜치 지역 결정 | Decision ID | Decision | Relation | Supporting Claims | Status | |---|---|---|---|---| ### 선언한 예외 | Override ID | Overrides | Reason | Approval | Status | |---|---|---|---|---| ### 가져온 artifact 계약 | Artifact Ref | Owner | Producer | Schema Ref | |---|---|---|---| ## 가져온 프로젝트 계약 | Ref | Owner | 요약 | Branch 적용 | |---|---|---|---| ### 수신한 위임 | Delegation Ref | From | Concern | Status | |---|---|---|---| ### 가져온 흐름 단계 | Stage Ref | Order | Owner | Input | Action | Output | |---|---:|---|---|---|---| ## 목표 - `WI-KEYCLOAK-PATTERNS-OVERVIEW-019`의 완료 조건을 구현한다: 4패턴 비교표의 모든 cell이 구현 WI evidence를 가리킨다 - 산출물: AP1~AP4 인증 통합 아키텍처의 **트레이드오프 매트릭스** — {토큰 위치 · 인증 강제/검증 주체 · XSS/CSRF surface · 선택 기준 · keycloak 설정} 을 한 표로, 각 cell 은 source claim 또는 구현 WI evidence 를 가리킨다 (§구현 가이드 1 이 표의 owner). ## 범위 ### 포함 범위 - Work Item 완료 조건 - 매트릭스 스켈레톤(행·열·이론 근거 cell) 작성 — 행 정의는 project `AUTH-TAXONOMY` 결정 소비, cell 근거는 raw source claim 직접 인용 (D1·D2·D4) - Evidence cell 채움 규칙 정의 — 의존 WI 완료 시 어떤 증거를 어떤 등급으로 링크하는지 (D3) ### 제외 범위 > 의도적으로 제외한 것. 면접 등에서 "이건 범위에 없었습니다"라고 답할 근거. - project decision registry 변경 - 각 패턴의 실 구현·함정 재현 — 의존 WI(003·008·010·012 및 그 자식들) 소유 (`OUT_OF_BRANCH_SCOPE`) - Google IdP brokering 을 별도 행으로 다루는 것 — cross-cutting 변형은 project §2.2 소유, 본 표에는 비고 1줄만 (D1) - 패턴별 세부 설정값(SameSite 값, NetworkPolicy 명세 등) — 해당 구현 WI branch 소유 (`OUT_OF_BRANCH_SCOPE`) ## 근거 (필수, 최소 1개+) > 이 branch의 구현·설계 결정의 **근거가 되는 외부 자료**. 공식 문서·대기업 기술 블로그·강의 등 raw 자료를 인용. | Source | 정당화하는 결정 | |---|---| | `[[raw/official-docs/oauth2-browser-based-apps-ietf-draft]]` | D1 행 구성(BFF/TMB/Browser-client 3패턴 정의 C1~C3) · D4 선택 기준 서열(C4 보안 내림차순, C5 TMB 경량 절충) | | `[[raw/official-docs/oauth2-proxy-overview-config-official]]` | D1 의 AP4 행(IETF 3종 밖 별도 패턴 — 검증된 C4 헤더 전달·C5 OIDC issuer 로 지지; reverse-proxy 일반동작 C1 은 source 재확인 실패로 `needs-confirmation` → 앵커 제외) · 매트릭스 AP4 행 cell(C2 옵션 활성 시 access 헤더 전달, C4 헤더 전달, C5 OIDC issuer 설정) | | `[[raw/official-docs/owasp-html5-storage-xss-spa]]` | D4 · 매트릭스 XSS surface 열의 AP1 행(C1 localStorage 세션 금지, C2 XSS 1건 전체 탈취) | | `[[raw/official-docs/spring-security-resource-server-jwt]]` | 매트릭스 AP1 행의 검증 주체·keycloak 설정 cell(C1 issuer-uri 검증, C6 audiences 검증) | | `[[raw/company-tech-blogs/curity-bff-pattern-spa]]` | D4 의 AP3 선택 조건(C1 토큰을 브라우저 밖에, C6 refresh 탈취 위험) — **vendor 사례, 공식 기준 승격 금지**(IETF C4 와 결합해서만 사용) | ## TODO 각 항목 옆에 증거 등급 표기: `actually-implemented` | `locally-verified` | `prod-verified` | `documented-only` | `planned` | `needs-confirmation` - [x] 매트릭스 스켈레톤 + source claim 근거 연결 (§구현 가이드 1) — 등급: `documented-only` - [ ] Evidence cell 에 적힌 모든 WI(anchor 003·008·010·012 + 자식 004·005·006·009·011·013·014) 완료 시 구현 증거 링크·등급으로 교체 (§구현 가이드 2 절차, D3 선택 조건) — 등급: `planned` - [ ] 4패턴 비교표의 모든 cell이 구현 WI evidence를 가리킨다 (완료 조건) — 등급: `planned` ## 진행 중 메모 - 2026-07-23 `NO_GROUND_TRUTH`: 구현 repo `/home/donghyeon/workspace/keycloak-pattern/`(단수) 는 README only — 구현 코드 없음(2026-07-25 재확인), 의존 anchor WI 4개(003·008·010·012) 전부 `planned`. 따라서 본 세션 산출은 **이론 골격 + 근거 연결까지** — Evidence cell 은 전부 `planned(WI-NNN)` placeholder. ## 결정 사항 > 추후 면접/회고에서 "왜 이렇게 했나" 답할 근거. 상세는 아래 `결정-근거 매핑` D-row 가 소유. - 2026-07-23: D1 행 = AP1~AP4 4행(Google 은 비고) / 이유: 인증 아키텍처 축이 canonical / 대안: 구 6패턴(배포×federation) 축 / 근거: `[[raw/official-docs/oauth2-browser-based-apps-ietf-draft]]` - 2026-07-23: D2 열 = project §5 의 5열 + Evidence 열 / 이유: 완료 조건이 cell→WI evidence 연결 요구 / 근거: 상속 `ACCEPTANCE-001@1` - 2026-07-23: D3 Evidence cell 규칙 = `planned(WI-NNN)` → 구현 후 `[[wi-slug]] · 등급` / 이유: 미검증 셀의 등급 과장 차단 / 근거: 상속 `ACCEPTANCE-001@1` + CLAUDE.md §6 - 2026-07-23: D4 선택 기준 열 = IETF 보안 내림차순 + 조건 분기 / 이유: 벤더 중립 서열 존재 / 대안: 벤더 블로그 권고 서열 / 근거: `[[raw/official-docs/oauth2-browser-based-apps-ietf-draft]]` ## Decision Evidence Map / 결정-근거 매핑 > `선택 조건` 열(R2): "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 `N/A`. | Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk | |---|---|---|---|---|---| | D1 | 매트릭스 행은 AP1~AP4 4행으로 구성하고, Google IdP brokering 은 행이 아니라 비고 1줄로 둔다. 행 정의 자체는 project `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1` 소비(재정의 아님). | 분류 축이 "누가 토큰을 쥐고 누가 인증을 강제하나"인 동안 이 결정 / 배포 토폴로지·federation 축으로 비교하려면 구 6패턴 표(project 가 2026-07-14 §2.3 으로 강등)로 회귀 | `raw/official-docs/oauth2-browser-based-apps-ietf-draft.md#OAUTH-BBA-C1`, `#OAUTH-BBA-C2`, `#OAUTH-BBA-C3`, `raw/official-docs/oauth2-proxy-overview-config-official.md#OAUTH2PROXY-C4`, `#OAUTH2PROXY-C5` | `official-standard` (IETF 3패턴) + `official-vendor-doc` (AP4 — 검증된 C4·C5; C1 은 소스 재확인 실패로 `needs-confirmation`, 앵커 제외) | AP4 는 IETF 3종 밖 — oauth2-proxy 가 draft 의 BFF 정의와 1:1 이라는 증거 없음(source 의 "Does not prove" 명시). AP4 행 각주로 경계 유지, 검증은 `검증해야 할 주장` #1 | | D2 | 매트릭스 열은 project §5 가 정한 {토큰 위치 · 검증 주체 · XSS/CSRF surface · 선택 기준 · keycloak 설정}([[raw/project-notes/keycloak-patterns-overview]] 소유)에 **Evidence 열 1개를 추가**한 6열로 한다. | 완료 조건이 "모든 cell 이 구현 WI evidence 를 가리킨다"인 동안 Evidence 열 필수 / 이론 비교표만 필요하면 5열로 충분하나 그 경우 본 WI 완료 조건 미충족 | 상속 `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1`, `raw/official-docs/oauth2-browser-based-apps-ietf-draft.md#OAUTH-BBA-C4` (선택 기준 열의 서열 축) | `project-decision` + `official-standard` | 열 이름·순서는 project §5 문구 개정 시 함께 갱신 필요(pointer 이므로 비차단 전파 대상) | | D3 | Evidence cell 은 `planned(WI-NNN)` placeholder 로 시작하고, 해당 WI 의 E2E + signature 함정 재현·해결 증거 확보 후에만 `[[raw/branch-notes/]] · locally-verified` 형태로 교체한다. `documented-only` 링크로는 완료 조건 미충족. | Evidence cell 에 적힌 WI 가 하나라도 `planned`/미검증인 동안 이 규칙 / **Evidence cell 에 적힌 모든 WI**(anchor 4개 + 각 행의 자식 WI: 004·005·006·009·011·013·014)가 `locally-verified` 도달 시 전 cell 교체 후 본 branch 완료 선언 | 상속 `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` (done-bar = E2E + 함정 재현·해결 evidence) | `project-decision` | `NO_GROUND_TRUTH`(2026-07-23): 구현 repo 자체가 미존재 — 등급 판정 기준이 실측 대상 없이 문서 규칙으로만 존재 | | D4 | '선택 기준' 열은 IETF 보안 내림차순(AP3 BFF > AP2 TMB > AP1 Browser-client; AP4 는 서열 밖 — 백엔드 인증코드 0줄 + 네트워크 격리 의존)을 기본 서열로 하고, 상황 분기(토큰 브라우저 노출 금지→AP3 / 프록시 전량 경유 부담→AP2 / polyglot 다수 백엔드 균일 인증→AP4[heuristic] / 학습·최단 셋업→AP1+PKCE)를 병기한다. | 벤더 중립 서열이 필요한 동안 IETF 서열 채택 / 특정 벤더 스택 고정 상황이면 해당 벤더 권고(예: curity)를 1차 근거로 쓸 수 있으나 본 프로젝트는 표준 우선 | `raw/official-docs/oauth2-browser-based-apps-ietf-draft.md#OAUTH-BBA-C4`, `#OAUTH-BBA-C5`, `raw/company-tech-blogs/curity-bff-pattern-spa.md#CURITY-BFF-C1`, `#CURITY-BFF-C6`, `raw/official-docs/owasp-html5-storage-xss-spa.md#OWASP-HTML5-C1`, `#OWASP-HTML5-C2` | `official-standard` 주도 + `company-case-study` 보조 | IETF 서열은 정성적("decreasing order of security") — 정량 근거 아님. curity C1("유일한 방법")은 vendor 과장 가능성 → IETF C4 와 결합해서만 인용 (`검증해야 할 주장` #4). polyglot→AP4 분기는 외부 claim 미보유(project §3-1-1·§기술 결정 AP4 행 documented-only 앵커 소비) — `UNSUPPORTED_IMPL_DECISION`(§구현 가이드 1 라벨 ③) | ## 구현 가이드 > **3-rule**: R1 Reference 필수 · R2 UNSUPPORTED_IMPL_DECISION 명시 · R3 OUT_OF_BRANCH_SCOPE 정제 (CLAUDE.md §15.5) ### 1. 4패턴 트레이드오프 매트릭스 (본 branch 의 deliverable 스켈레톤) > **Trace**: 행 구성=D1(OAUTH-BBA-C1~C3, OAUTH2PROXY-C4·C5) · 열 구성=D2(ACCEPTANCE-001@1) · '선택 기준' cell 내용=D4(OAUTH-BBA-C4·C5, CURITY-BFF-C1·C6, OWASP-HTML5-C1·C2) · Evidence cell=D3 > > - **UNSUPPORTED_IMPL_DECISION**: ① Evidence placeholder 표기를 `planned(WI-NNN)` 텍스트로 통일 — 근거 raw 는 표기 형식을 권고하지 않음. trade-off: 후속 스크립트/사람이 `planned(` prefix 로 미완 cell 을 grep 가능. ② AP4 토큰 위치 cell 의 "세션은 프록시, 백엔드·브라우저 토큰 0" — 세션 저장 위치를 진술하는 official claim 미보유, project §2.1 AP4 행(project-decision) 소비 + 기본 구성 가정. ③ D4 의 polyglot 다수 백엔드→AP4 분기 — 외부 source claim 미보유이나 project §3-1-1 P1A 트레이드오프 + §기술 결정 AP4 행("polyglot 균일 적용", project-decision·documented-only) 앵커 소비. 순수 heuristic 아님 — 단 정량 근거는 없음. | AP | 토큰 위치 | 인증 강제·검증 주체 | XSS/CSRF surface | 선택 기준 | keycloak 설정 | Evidence | |---|---|---|---|---|---|---| | **AP1** SPA-direct + RS | 브라우저(JS) — `OAUTH-BBA-C3` | SPA(public client+PKCE)가 토큰 획득, RS 가 JWT `iss`/`aud` 검증 — `SSRS-JWT-C1`·`C6` | XSS 1건 = 저장 토큰 전체 탈취; localStorage 세션 보관 금지 — `OWASP-HTML5-C1`·`C2` | 학습·최단 셋업, 프론트가 OIDC 완전 제어; 보안 서열 최하 — `OAUTH-BBA-C4` | public client + PKCE, RS `issuer-uri`/`audiences` — `SSRS-JWT-C1`·`C6` | `planned(WI-003·004·005·006)` | | **AP2** Token-Mediating Backend | access→브라우저, refresh→백엔드만 — `OAUTH-BBA-C2` (refresh 보관 위치 문언은 draft §6.2 서술, 실측은 검증 #2·WI-009) | 백엔드(confidential client)가 토큰 획득, 브라우저가 RS 직접 호출 — `OAUTH-BBA-C2` | refresh 는 보호되나 access 는 브라우저 노출 — `OAUTH-BBA-C5`, `CURITY-BFF-C6` | BFF 전량 프록시 부담 없는 경량 절충(BFF 보다 덜 안전, browser-client 보다 안전) — `OAUTH-BBA-C5` | confidential client(+secret), 브라우저에 access 만 전달 — `OAUTH-BBA-C2` | `planned(WI-008·009)` | | **AP3** BFF | 백엔드 세션(브라우저 토큰 0개) — `OAUTH-BBA-C1`, `CURITY-BFF-C3` | 백엔드(confidential client)가 인증+전량 프록시 — `OAUTH-BBA-C1` | 토큰 XSS 면역 — `CURITY-BFF-C4`(httpOnly cookie); cookie 자동첨부 → CSRF surface(방어는 WI-011 소유) — 논리 도출(project §3-2 AP3 시퀀스 CSRF alt-block; C4 는 Does-not-prove 로 CSRF/SameSite 미지지) | 토큰 브라우저 노출 금지 요건일 때; 보안 서열 최상 — `OAUTH-BBA-C4`, `CURITY-BFF-C1` | confidential client + httpOnly session cookie — `CURITY-BFF-C4` | `planned(WI-010·011)` | | **AP4** Edge forward-auth | 프록시 세션(백엔드·브라우저 토큰 0 — `--pass-access-token` 미사용 기본 구성 시, `OAUTH2PROXY-C2` 옵션 활성 시 access 가 upstream 헤더로 전달됨) — project §2.1 AP4 행 소비, `UNSUPPORTED_IMPL_DECISION` ② | 별도 reverse proxy 가 OIDC 수행, 백엔드는 전달 헤더 신뢰(인증코드 0줄) — `OAUTH2PROXY-C4`·`C5` | `X-Forwarded-User` 위조 — 헤더 신뢰 모델에서 논리 도출(위협 재현·차단은 WI-014 완료 조건 소유, project §8.0) | polyglot 다수 백엔드에 균일 인증[heuristic — UNSUPPORTED ③]; IETF 3종 밖 실무 패턴 — D1 Open Risk 참조 | proxy `--oidc-issuer-url` (discovery), `--pass-user-headers` — `OAUTH2PROXY-C4`·`C5` | `planned(WI-012·013·014)` | - 비고(행 아님): **Google IdP brokering** 은 어느 AP 에도 realm 설정만으로 얹히는 cross-cutting 변형 — 정의·함정은 [[raw/project-notes/keycloak-patterns-overview]] §2.2 소유. - R3 정제: 각 cell 의 세부 방어 구현(SameSite 값, NetworkPolicy 명세, audience validator 코드)은 Evidence 에 적힌 구현 WI branch 소유 — 본 표는 pointer 만 유지. ### 2. Evidence cell 채움 절차 (구현 WI 완료 시) > **Trace**: D3 (ACCEPTANCE-001@1 도출). 절차만 정의 — 실행은 각 WI 완료 시점. > > - **UNSUPPORTED_IMPL_DECISION**: cell 교체 단위를 "anchor WI 묶음"이 아니라 **개별 WI**로 함 — 근거 raw 없음. trade-off: 부분 진행을 표에 즉시 반영(전량 대기 시 표가 오래 stale). 다중-WI cell 의 혼합 상태 표기 예: `[[raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce]] · locally-verified + planned(WI-004·005·006)`. 1. 해당 WI branch 의 `## 완료 후 정리` 에서 E2E 증거(200 OK 로그/스크린샷) + signature 함정 before/after 확인. 2. 증거 등급 판정 — `locally-verified` 이상만 인정 (`documented-only` 는 완료 조건 미충족, D3). 3. cell 의 `planned(WI-NNN)` → `[[raw/branch-notes/]] · locally-verified` 로 교체. 4. 전 cell 교체 완료 시 본 branch TODO 최종 항목 체크 → `/ingest` 로 `wiki/projects/` 추출 후보. ## 엣지·실패·의존 - **실패·엣지 경로**: - 의존 WI 부분 완료 — cell 단위 독립 교체(§구현 가이드 2), 미완 cell 은 `planned(...)` 유지. 표 전체를 블록하지 않음. - project taxonomy 개정(`AUTH-TAXONOMY-001` @2 발행) — packet 이 @1 고정이므로 preflight 가 `STALE_INHERITANCE_REVISION` 으로 차단 → 행 구성 재검토 후 packet 재생성. - AP4↔IETF-BFF 매핑 드리프트 발견(검증 #1 실패) — 매트릭스 AP4 행 각주 갱신 + D1 Open Risk 재판정. 표 삭제 아님. - **다른 계약 의존**: `WI-KEYCLOAK-PATTERNS-OVERVIEW-003`(AP1 anchor), `WI-KEYCLOAK-PATTERNS-OVERVIEW-008`(AP2), `WI-KEYCLOAK-PATTERNS-OVERVIEW-010`(AP3), `WI-KEYCLOAK-PATTERNS-OVERVIEW-012`(AP4) — frontmatter `depends_on` 은 이 anchor 4개(project registry row 소유). Evidence cell 은 anchor 의 자식 WI(004·005·006·009·011·013·014)도 인용하므로 **완료 선언은 cell 에 적힌 모든 WI 기준**(D3 선택 조건). anchor 완료 조건이 바뀌면 §구현 가이드 2 의 판정 기준도 재검토. ## 검증해야 할 주장 / Claims To Verify | Claim | Why uncertain | How to verify | Status | |---|---|---|---| | AP4(oauth2-proxy)가 IETF BFF 요건(draft §6.1.3)과 어디서 갈라지는지 | source 가 "1:1 매핑 미증명"을 명시(OAUTH-BBA usage boundary) | WI-012 구현 후 draft §6.1.3 MUST 항목 체크리스트 대조 | `needs-confirmation` | | AP2 에서 refresh token 이 브라우저 network 응답에 나타나지 않는다 | IETF 정의일 뿐 우리 구현의 실측 아님 | WI-009 완료 조건(network 탭/response body 검사)으로 검증 | `planned` | | 매트릭스 각 행의 이론 서술이 single-EC2 실구현에서 재현된다 | `NO_GROUND_TRUTH` — 구현 repo 미존재, anchor WI 전부 planned | 의존 WI 4개의 E2E + 함정 재현 evidence 로 cell 단위 검증 | `planned` | | "토큰을 브라우저 밖에 두는 것이 유일한 보호 방법"(curity C1)의 일반화 | vendor 블로그 표현 — IETF 는 3패턴 모두 trade-off 로 허용(C4) | IETF draft §6.3.2 방어 요건과 대조해 한정 서술(AP3 선택 조건)로만 유지 | `needs-confirmation` | ## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때) `/coverage` 실행 전. ## 마주친 문제 아직 없음. ## 묶음 (이 branch에서 파생된 자료) ## 관련 일일 노트 해당 없음. ## 완료 후 정리 - PR 링크: - 리뷰 메모: