Files
llm-wiki/raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation.md
T

62 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-internal-spa-direct-google-federation (P2B Internal SPA + Resource Server + Google federation) branch-note raw BR-KEYCLOAK-CHILD-028FAA28 branch-child keycloak-patterns-overview WI-KEYCLOAK-PATTERNS-OVERVIEW-020
DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1
1 feature-keycloak-internal-spa-direct-google-federation feature-keycloak-patterns
keycloak-patterns
branch
keycloak-patterns
auth
oauth2
oidc
2026-05-25 in-progress 378fb324f192fa41f903d9d9158a9ce7318aa645769b57283fdd8de553efd5af

branch: feature-keycloak-internal-spa-direct-google-federation (P2B — Internal SPA + Resource Server + Google federation)

Layer: raw/branch-notes/ — root raw/branch-notes/feature-keycloak-patterns의 sub-branch. P2B = P2A (내부 배치 SPA + Resource Server, public client, Authorization Code + PKCE) + Google IdP brokering. 비교축:

본 노트의 역할 (2026-07-17 /branch-spec 정리): P2B 는 구성(composition) 허브다. brokering 의 개별 관심사(First Broker Login Flow · claim/attribute mapping · Google client 등록 · 3-leg trust)는 각각 owner 브랜치가 소유하며, 본 노트는 rules/consistency-contract.mdReference-Only 규약에 따라 포인터 + 1줄 요약으로만 인용한다. 본 노트가 직접 소유하는 결정은 D5 · D8 · D9 · D10 이다.

부모 (필수)

raw/branch-notes/feature-keycloak-patterns

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: project governance hub가 AP1~AP4 taxonomy와 child progress index를 유지한다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1 canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 SPA Direct와 Google federation의 조합을 AP1 및 brokering cross-cutting 변형으로 분류한다 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에 Google federation을 추가했을 때 흐름이 어떻게 바뀌는지 정확히 이해. 핵심 통찰:

SPA 입장에선 Keycloak만 통신한다. Google과 직접 통신하지 않는다. Google 통신은 Keycloak 내부(server-to-server)에서만 발생한다. SPA 코드 변경 거의 0.

이게 IdP brokering의 우아함이다. P2A에서 추가되는 건:

  • Keycloak 관리자 설정 (Identity Provider 등록, Mappers 구성, First Broker Login Flow 정책)
  • Google Cloud Console에서 OAuth Client 등록 (redirect URI = Keycloak의 broker endpoint)

SPA keycloak-js 초기화 코드, 백엔드 Resource Server JWT validation 코드, audience/issuer 확인 로직은 그대로.

면접 질문: "Google 로그인이 붙으면 SPA 코드 어디가 바뀌나요?" → "거의 안 바뀝니다. Keycloak이 Google을 자기 안으로 broker하기 때문에 SPA가 보는 token은 여전히 Keycloak token입니다. 변경 지점은 Keycloak 관리자 설정이고, 운영 부담은 사용자 매핑(First Broker Login Flow)과 claim mapping에 있습니다."

  • 이슈:
  • PR:

범위

포함 범위

  • P2B 구성(composition) 자체 — P2A + Google brokering 을 세우는 순서와 각 단계의 owner 브랜치 배선 (§구현 가이드 §1).
  • P2A→P2B 변경량(zero-change) 논증 — 어느 레이어가 안 바뀌는지의 검증 지점 (§구현 가이드 §2, D8).
  • federation 축 ⟂ token 보유 축의 직교성 논증 — brokering 비용은 P1B/P2B 동일, 차이는 브라우저 token 보유뿐 (D9).
  • Keycloak Identity Broker SPI 미사용 결정 — 구성요소 결정 (D5).
  • P2B 배포 realm 전제의 선언 — 학습 realm 의 SMTP 미설정 사실과 그로 인한 Verify-Existing-Account 폴백 (D10). owner 가 "배포 realm 사실"로 범위 밖에 둔 결정 변수를 hub 가 소유.
  • 3-leg trust / claim mapping / First Broker Login 의 배선과 인용 — 정책 자체는 owner 브랜치 소유, 본 노트는 조립만.

제외 범위

의도적으로 제외. 아래는 모두 owner 브랜치가 소유 — 본 노트는 결정하지 않고 consume 한다 (Reference-Only).

근거 (필수, 최소 1개+)

본 sub-branch의 P2B (Internal SPA + Google federation) 채택 근거. 상세 대안 비교는 §외부 근거 / 대안 조사 참조.

Source 정당화하는 결정
raw/official-docs/keycloak-identity-brokering-overview-official Keycloak Identity Broker overview — Google IdP brokering 채택 근거 (D8)
raw/official-docs/keycloak-first-broker-login-flow First Broker Login Flow — 사용자 매핑 시점 결정 근거 (D2, 위임)
raw/official-docs/google-oidc-discovery-spec Google OIDC discovery — Google IdP 표준 동작 근거
raw/official-docs/keycloak-identity-provider-mappers IdP Mappers — claim mapping 근거 (D6, 위임)
raw/official-docs/keycloak-identity-broker-spi Custom Identity Broker SPI — 대안 (현재 unused, D5)
raw/official-docs/oauth2-browser-based-apps-ietf-draft (2026-07-17 추가) IETF BCP — BFF > Token-Mediating > Browser-based client 보안 순서. D9 (P2B 의 token 보유 수용) 의 공식 근거
raw/official-docs/owasp-html5-storage-xss-spa (2026-07-17 추가) 단일 XSS 로 storage 전량 탈취 — D9 의 위협 모델 근거
raw/official-docs/keycloak-first-broker-login-verify-authenticators-official (2026-07-17 추가) AutoLink = 별도 opt-in dangerous authenticator (공식 WARNING). 본 노트 2026-05-25 prose 의 정정 근거
raw/official-docs/keycloak-identity-provider-sync-mode-official (2026-07-17 추가) Sync Mode import/force 정의. D3 위임처의 근거
raw/official-docs/spring-security-resource-server-jwt Resource Server 의 issuer-uri 검증 — D7 (위임) 의 근거

외부 근거 / 대안 조사 (2026-05-25 — P2B Internal SPA + Google IdP Brokering)

본 sub-branch의 P2A + Google IdP Brokering 채택에 대한 외부 source. P2A에 federation을 추가하는 방식 비교.

  • 채택 결정 (Keycloak Identity Brokering + Identity Provider Mappers + First Broker Login Flow):
  • 검토한 대안:
    • 대안 1: Google OIDC 직접 (Keycloak 우회) — SPA가 Google OIDC accounts.google.com에 직접 요청. 장: Keycloak 운영 부담 0 / 단: 다중 IdP(예: GitHub, SAML) 통합 시 SPA 코드 분기 폭증.
    • 대안 2: AWS Cognito User Pools + Google federation — AWS Cognito가 broker 역할. 장: managed / 단: vendor lock-in.
    • 대안 3: Auth0 social connections — Auth0가 Google + Facebook + GitHub 등 통합. 장: 운영 부담 최소 / 단: 비용 + vendor lock-in.
    • 대안 4: Firebase Authentication — Google 자사 IdP managed. 단: Firebase 종속.
    • 대안 5: SAML federation (Google Workspace SAML) — 엔터프라이즈 환경. 그러나 일반 사용자 Google 계정에는 OIDC가 표준.
  • 비교 핵심: IdP Brokering의 가치는 P1B와 동일하지만, SPA 컨텍스트에서 특히 중요: SPA 코드는 Keycloak만 알면 되고, "Sign in with Google" 버튼은 Keycloak 로그인 화면이 제공 → SPA가 IdP 종류를 모름. Claim mapping으로 Google의 email, picture, name → Keycloak attribute / role 매핑 가능. 3-leg trust (Browser ↔ Keycloak ↔ Google) — 각 단계 검증 필요. First Broker Login Flow의 default Auto-Link은 보안 위험 (P1B와 동일) — Confirm Link Existing Account로 변경.

정정 (2026-07-17) — 위 "비교 핵심" 마지막 문장(default Auto-Link은 보안 위험 … Confirm Link Existing Account로 변경)은 사실과 다르다. OOTB 기본 경로는 이미 Handle Existing Account(Confirm Link)이며, Automatically Set Existing User(AutoLink)는 기본값이 아니라 관리자가 별도로 추가하는 opt-in dangerous authenticator다 — 공식 WARNING: raw/official-docs/keycloak-first-broker-login-verify-authenticators-official.md#KC-FBLVERIFY-C4. 따라서 "AutoLink → Confirm Link 로 변경"이라는 조치는 불필요하며, 실제 결정은 "AutoLink 를 추가하지 않는다"이다. 권위 있는 서술은 owner 노트 raw/branch-notes/feature-keycloak-first-broker-login-flow D1 — AutoLink 미사용/DISABLED, OOTB 기본은 Confirm Link. 원문은 학습 이력 보존을 위해 verbatim 유지한다(덮어쓰지 않음). 상세는 §Audit & Findings CONTRADICTION-1.

TODO

  • P2A sub-branch 작성 후 코드 변경량 정량 비교 (실 LoC diff) — 등급: planned
  • Keycloak admin UI에서 Google IdP 등록 스크린샷 캡처 (학습 자료) — 등급: planned
  • First Broker Login Flow custom (비밀번호 확인 후 link) authenticator 설정 절차 정리 — 등급: planned
  • XSS 시 token 탈취 시나리오 정리 (P1B와의 본질적 차이) — 등급: planned
  • Mapper 세부 종류표 공식 문서 재확인 (needs-confirmation 해소) — 등급: planned
  • (2026-07-18 정합) attribute default IMPORT와 role mapper override FORCE의 적용 계층을 owner 문서에서 분리하고 본 hub는 포인터만 유지 — 등급: documented-only
  • (2026-07-17 추가, 우선) CVE-2026-9087 공식 advisory 를 wiki-source-summarizerraw/official-docs/ 에 아카이브 — 현재 sibling 노트 2곳의 자기 보고만 있어 §구현 가이드 §1 0단계가 needs-confirmation 게이트에 머묾. 아카이브 후 영향/patch 버전을 FACT 로 승격 — 등급: needs-confirmation
  • (2026-07-17 추가) P1B ↔ IETF draft BFF 정의 매핑 검증 (D9 의 "P2B < P1B" 를 표준 권위로 말할 수 있는지) — 등급: planned
  • (2026-07-17 추가) CSRF / state 방어의 owner 지정 — draft §6.3.2 가 browser-based client 에 CSRF 방어 MUST 를 요구하고 근거 raw 가 "P2 sub-branch 에서 별도 검증"으로 지목하는데, P2A·P2B 어느 노트도 Decision 으로 소유하지 않음(P2A 는 미체크 체크리스트 항목뿐). #OAUTH-BBA-C3 의 허용이 조건부이므로 D9 의 전제이기도 함 — 등급: planned
  • (2026-07-18 정합) P2A D4는 본 노트 D8 pointer로 전환해 zero-change owner를 본 hub 하나로 고정 — 등급: documented-only

진행 중 메모

  • 2026-07-17 (/branch-spec 채움 pass) — 본 노트는 2026-05-25 작성분으로, 이후 2026-07-14~16 에 brokering 의 개별 관심사를 다루는 owner 브랜치들이 대거 작성되며 본 노트의 결정 대부분이 owner 를 획득했다. 따라서 이번 pass 의 핵심 작업은 새 결정을 추가하는 것이 아니라 재진술(restatement)을 Reference-Only 포인터로 강등하는 것이었다 (rules/consistency-contract.md Single-Owner).
  • 자동조사(wiki-decision-researcher) 미실행 — 본 노트의 needs-confirmation 결정들이 기다리던 근거가 이미 raw 에 아카이브되어 있었다(2026-07-14~16 수집분: oauth2-browser-based-apps-ietf-draft · keycloak-identity-provider-sync-mode-official · keycloak-first-broker-login-verify-authenticators-official · keycloak-identity-provider-trust-email-official). 새 조사 대신 기존 아카이브 재앵커링으로 해소 — 조사 0건, deferred 0건.
  • D9 가 이번 pass 의 최대 수확OAUTH-BBA-C4 (IETF BCP 가 BFF → Token-Mediating → Browser-based Client 를 보안 감소 순서로 명시) 는 그동안 "XSS 위험이 높다"는 정성적 서술에 머물던 P2B vs P1B 비교에 official-standard 등급의 순서 근거를 부여한다. 2026-05-25 시점엔 이 source 가 없었다.
  • 코드 repo 가 없으므로 §구현 가이드는 코드 명세가 아니라 admin 구성 절차 + owner 브랜치 배선 순서로 작성했다. 모든 항목 등급은 documented-only 또는 planned.

결정 사항 (decisions)

외래 결정은 Reference-Only로 유지한다. 과거 상세 결정문은 각 owner의 history에서 추적한다.

컴포넌트 다이어그램

Browser (SPA, public client)
   │
   │  (1) GET /index.html
   │  (2) keycloak-js → Authorization Code + PKCE 시작
   ▼
Keycloak (Authorization Server)
   │ ┌─ 사용자가 "Google 로그인" 버튼 클릭 ─┐
   │ │ (3a) Keycloak이 /broker/google/login으로 redirect
   │ ▼                                  │
   │ Google OIDC (accounts.google.com)  │
   │ │ (3b) Google 로그인 + consent     │
   │ │ (3c) authorization code → Keycloak broker endpoint
   │ ▼                                  │
   │ Keycloak ↔ Google (server-to-server)│
   │   - token endpoint POST            │
   │   - ID token signature 검증 (JWKS)│
   │   - email_verified, hd 정책 검사   │
   │   - First Broker Login Flow:       │
   │       - 기존 user 찾기 / 신규 생성 │
   │       - Account Linking 결정       │
   │   - IdP Mappers 적용 (claim → attribute / role)
   │ └─────────────────────────────────┘
   │
   │  (4) Keycloak이 자체 Authorization Code 발급 → SPA로 redirect
   ▼
Browser (SPA)
   │  (5) code → POST /token (PKCE verifier 동봉)
   │  (6) Keycloak access_token(JWT) + refresh_token + id_token 수신
   ▼
Backend (Resource Server, Spring Boot 등)
   │  (7) Authorization: Bearer <Keycloak access_token>
   │  (8) JWKS로 signature 검증 + iss/aud/exp 확인
   ▼
   200 OK

핵심: 백엔드는 Keycloak token만 본다. Google ID token은 Keycloak 안에서 검증되고 폐기된다 (필요 시 broker endpoint로 회수 가능, 그러나 본 패턴에선 사용 안 함).

토큰 교환 sequence (P2A 1-7 + brokering 삽입)

# From To Payload 비고
1 Browser Keycloak /realms/{r}/protocol/openid-connect/auth client_id, redirect_uri, code_challenge, scope=openid email profile, state P2A와 동일
2 Keycloak Browser 로그인 페이지 (HTML) — "Sign in with Google" 버튼 포함 IdP 등록 시 자동 노출
3a Browser Keycloak /realms/{r}/broker/google/login (사용자가 Google 버튼 클릭) P2B 추가
3b Keycloak Browser 302 → https://accounts.google.com/o/oauth2/v2/auth?... (Keycloak이 Google client_id, redirect_uri=Keycloak broker endpoint, scope, state, nonce 동봉) P2B 추가
3c Browser Google 로그인 + consent P2B 추가
3d Google Browser 302 → Keycloak /realms/{r}/broker/google/endpoint?code=... P2B 추가
3e Keycloak Google /token code, client_id, client_secret (server-to-server) P2B 추가
3f Google Keycloak Google access_token + id_token (RS256) P2B 추가
3g Keycloak Google JWKS (캐시된 키로) ID token signature 검증 P2B 추가
3h Keycloak (internal) First Broker Login Flow 실행 → user 매핑/생성 → Mappers 적용 P2B 추가
4 Keycloak Browser 302 → SPA redirect_uri + Keycloak code P2A와 동일
5 Browser (SPA) Keycloak /token grant_type=authorization_code, code, code_verifier (PKCE) P2A와 동일
6 Keycloak Browser Keycloak access_token (JWT, RS256) + refresh_token + id_token P2A와 동일
7 Browser Backend /api/... Authorization: Bearer <access_token> P2A와 동일
8 Backend Keycloak JWKS (캐시) P2A와 동일

P1B와의 차이: P1B는 oauth2-proxy가 token을 보유 (브라우저에 cookie). P2B는 브라우저가 직접 token 보유. brokering 부분(3a-3h)은 둘이 동일.

장점 / 단점

vs P2A (no Google)

항목 P2A P2B
Google 계정으로 로그인
SPA 코드 변경 거의 0 (button label 정도)
백엔드 코드 변경 0 (여전히 Keycloak JWT만 검증)
운영 부담 Keycloak realm/client만 Keycloak realm/client + Google Cloud OAuth + IdP Mappers + First Broker Login Flow 정책
사용자 매핑 정책 불필요 (Keycloak 자체 가입) 필수 (Account Linking, email_verified 정책 등)
신뢰 경계 Browser ↔ Keycloak (2-leg) Browser ↔ Keycloak ↔ Google (3-leg)
토큰 revocation Keycloak refresh token revoke 동일 (Google revoke는 별개)

vs P1B (Edge proxy + Google)

항목 P1B (oauth2-proxy 패턴) P2B (SPA Direct)
브라우저의 token 보유 ✗ (cookie session만) ✓ (sessionStorage/메모리)
XSS risk 낮음 (token이 브라우저 JS 접근 밖) 높음 (XSS 시 token 탈취 가능)
backend 추가 proxy 필요 proxy 불필요
SPA가 OIDC 처리 모름 (proxy가 처리) 직접 처리 (keycloak-js 등)
brokering 흐름 동일 동일
token revocation proxy session 무효화 (즉시) Keycloak refresh token revoke (access token은 만료까지 유효)

요약: brokering 추가의 운영 비용은 P1B/P2B 모두 같다. 두 패턴 차이는 "브라우저가 token을 보느냐"이며 이는 federation과 직교한다.

(2026-07-17 근거 보강) 위 요약의 "직교" 논증은 D9 로 승격됐고, 이제 부분적으로 공식 근거를 가진다. IETF oauth2-browser-based-apps draft 는 세 패턴을 보안 감소 순서로 제시한다 — BFF → Token-Mediating Backend → Browser-based OAuth 2.0 Client (#OAUTH-BBA-C4, verbatim: "presented in decreasing order of security").

표준이 확정한 것 vs 본 노트가 매핑한 것을 분리한다 — 아래 ②·③에 ①의 권위를 빌려주면 안 된다:

  1. 표준이 확정 (추상 수준): 세 패턴의 보안 감소 순서 자체 (#OAUTH-BBA-C4). draft 는 추상 3분류를 정의하고 순서를 매길 뿐, 우리 프로젝트의 P1A~P3B 6조합을 이 분류에 배정해주지 않는다.
  2. 본 노트의 매핑 판정 — 강함: P2B ∈ Browser-based OAuth 2.0 Client (#OAUTH-BBA-C3: 브라우저 앱이 public client 로 모든 OAuth 책임을 지고 token 을 직접 보유 → P2B 서술과 축자 일치). 근거는 견고하나 표준이 확정해준 것은 아니다.
  3. 본 노트의 매핑 판정 — 약함: P1B(oauth2-proxy / Traefik ForwardAuth) ∈ BFF 계열. 근거 raw 가 이 매핑을 특정해 부인한다 — raw/official-docs/oauth2-browser-based-apps-ietf-draft.md "Does not prove": P1A/P1B/P2A/P2B/P3A/P3B 6개 구체적 조합이 이 3분류와 1:1로 정확히 대응한다는 것 — 특히 P1(oauth2-proxy/ForwardAuth)이 이 draft 의 "BFF" 정의와 정확히 같은 개념인지는 별도 확인 필요.

따라서 "P2B < P1B" 는 표준 권위로 단정할 수 없다 — 그 단정은 ②와 ③ 둘 다 참이어야 성립하며, raw 는 ②·③ 모두를 1:1 대응 부인 목록에 넣었다. 두 매핑 각각을 §Claims To Verify 로 분리 검증한다.

그래도 D9 의 착수 판단은 무너지지 않는다: D9 가 실제로 요구하는 것은 "P2B 는 브라우저에 token 을 두므로 그만큼 노출을 수용한다"이고, 이는 ②(축자 일치)만으로 성립한다. P1B 와의 상대 순서는 D9 의 대안 분기("XSS 가 유의하면 P1B 로 이동")에만 필요하며 그 분기는 미검증 상태다. 또한 이 순서는 Google federation 유무를 변수로 다루지 않으므로 "직교" 주장 역시 본 노트의 추론이며 §Claims To Verify 대상이다.

claim mapping (Google → Keycloak)

P2B composition 관점의 한 줄 불변식: Google claim은 Keycloak 내부 user/role로 정규화된 뒤, SPA와 backend에는 Keycloak이 발급한 token만 노출된다.

신뢰 경계 (3-leg trust)

P2B composition 관점의 한 줄 불변식: Google↔Keycloak 검증과 Keycloak↔Backend 검증은 서로 다른 hop이며, backend의 trust anchor는 Keycloak이다.

결정-근거 매핑

P2B 는 구성 허브다. 아래 D1~D4 · D6 · D7 은 DELEGATED — owner 브랜치가 결정을 소유하고 본 노트는 Reference-Only 포인터 + 1줄 요약만 보유한다 (rules/consistency-contract.md). 본 노트가 직접 소유하는 결정은 D5 · D8 · D9 뿐이다. Decision ID 는 2026-05-25 판과 동일하게 유지한다(외부 참조 안정성). 위임된 행도 ID 를 재사용 번호로 남긴다.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 DELEGATEDraw/branch-notes/feature-keycloak-first-broker-login-flow D4 — silent auto-link 차단 owner 참조 owner 참조 delegated hard-reject SPI variant는 현재 미구현
D2 DELEGATEDraw/branch-notes/feature-keycloak-first-broker-login-flow D2 — 기존 계정 link에 소유 증명 적용 본 realm의 SMTP 분기는 아래 D10이 조립 owner 참조 delegated owner의 lockout risk 승계
D3 DELEGATEDraw/branch-notes/feature-keycloak-google-claim-attribute-mapping D3 — IdP-level profile default IMPORT role freshness는 raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role D1의 mapper override owner 참조 delegated runtime override 우선순위는 needs-confirmation
D4 DELEGATEDraw/branch-notes/feature-keycloak-idp-mappers-claim-to-role D3 — 학습 단계 hd 제한 미적용 owner 참조 owner 참조 delegated personal account 처리는 owner 검증 대상
D5 OWNED — Keycloak Identity Broker SPI 미사용. Google built-in social provider + Mappers 로 충분 built-in social provider 가 대상 IdP 를 지원(Google=지원)하면 SPI 미사용. 대안(SPI 작성): 프로토콜이 OIDC/SAML 이 아니거나, built-in 이 제공 못 하는 비표준 claim 처리·custom 인증 단계가 필요할 때 raw/official-docs/keycloak-identity-brokering-overview-official.md#KC-IDP-BROKER-C1, raw/official-docs/keycloak-identity-broker-spi.md#KC-BROKER-SPI-C1, #KC-BROKER-SPI-C2 official-vendor-doc SPI 검토 trigger 의 정량 기준 부재 — "built-in 이 부족한 시점"이 학습 단계 가정에 의존 (Should-fix, 운영 전환 시 재평가)
D6 DELEGATEDraw/branch-notes/feature-keycloak-google-claim-attribute-mapping D4 — profile attribute mapping owner 참조 owner 참조 delegated link key는 raw/branch-notes/feature-keycloak-account-linking-sub-vs-email D1
D7 DELEGATEDraw/branch-notes/feature-keycloak-three-leg-trust-chain D1 — backend trust anchor는 Keycloak owner 참조 owner 참조 delegated owner의 미검증 항목 승계
D8 OWNED — P2B 의 federation 은 Keycloak 레이어에서만 broker (SPA 는 Google 과 직접 통신하지 않음). 메커니즘: IdP 를 realm 에 등록하면 Keycloak 로그인 페이지가 버튼을 자동 제공하므로 SPA 코드가 IdP 를 몰라도 됨 IdP 가 1개 초과로 늘어날 가능성이 있거나 IdP 종류를 앱에서 숨기고 싶으면 brokering. 대안: IdP 가 영구히 Google 1개 + Keycloak 운영 부담을 피하고 싶다 → SPA 가 Google OIDC 직접(§외부 근거 대안 1). managed 선호 → Cognito/Auth0/Firebase(대안 2~4). 경계: Hide on Login Page=ON 이면 버튼이 안 뜨고 앱이 kc_idp_hint 를 보내야 함 → 그 순간 zero-change 전제가 깨지고 자식 raw/branch-notes/feature-keycloak-federation-spa-zero-change D1 (idpHint 미사용) 의 대안 경로로 이동 raw/official-docs/keycloak-identity-brokering-overview-official.md#KC-IDP-BROKER-C1 (broker delegation 모델, L0/L1); (2026-07-17 보강 — L1/L2) raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official.md#KC-HIDELOGIN-C2 (IdP 구성 시 로그인 페이지에 로그인 옵션으로 나타남 = zero-change 의 메커니즘, verbatim), #KC-HIDELOGIN-C1 (realm 의 등록된 모든 IdP 를 앱이 사용 가능·기본 활성 → 앱별 코드 불요), #KC-HIDELOGIN-C3 (ON 일 때만 미노출 + kc_idp_hint 대안 = 조건·경계 L2), raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C1 (Google 등록 경로) official-vendor-doc (L1~L2) ① "IdP 가 늘어난다"는 전제가 학습 프로젝트에선 가정 — 실제 다중 IdP 요구 미발생 시 대안 1 이 더 단순 (Advisory). ② Hide on Login Page 토글의 신규 등록 시 기본 상태KC-HIDELOGIN-C3 원문이 직접 진술하지 않음 → 기본이 ON 이면 zero-change 전제 붕괴. 자식 노트가 동일 caveat 를 needs-confirmation 으로 보유 → visual verify 필수 (§Claims To Verify)
D10 OWNED (2026-07-17 신규 — hub 가 소유하는 배포 realm 사실) — P2B 학습 realm 은 SMTP 를 설정하지 않는다Verify Existing Account By Email 을 쓸 수 없어 Re-authentication 이 자동 폴백 학습 스택(메일 서버 없음) → SMTP 미설정 → owner 의 fork 중 "Re-authentication" 가지가 자동으로 선택됨(관리자 조치 불요). 대안: 운영/소비자 서비스로 전환해 SMTP 를 붙이면 → Verify Existing Account By Email 이 기본(ALTERNATIVE)이 되므로, password 소유 증명을 관철하려면 그때 email authenticator 를 명시 DISABLE 해야 함 → raw/branch-notes/feature-keycloak-first-broker-login-flow D2 raw/official-docs/keycloak-first-broker-login-verify-authenticators-official.md#KC-FBLVERIFY-C3 (Re-auth = "email authenticator 를 쓸 수 없을 때(예: realm 에 SMTP 미설정)만 쓰는" 폴백, verbatim), #KC-FBLVERIFY-C1 (Email = SMTP 시 기본) official-vendor-doc + documented-only (배포 사실) SMTP 미설정은 부재 근거: raw/branch-notes/feature-keycloak-docker-compose-stack 이 SMTP/mail 을 일절 구성하지 않음에서 도출했을 뿐, 스택 노트가 "SMTP 없음"을 명시 선언하지는 않는다 → 실 스택 기동 시 realm SMTP 설정란 확인 필요 (§Claims To Verify). owner 는 이 realm 사실을 자기 범위 밖(UNSUPPORTED_IMPL_DECISION)으로 명시 배제했으므로 hub 인 본 노트가 소유한다
D9 OWNED — P2B 는 브라우저 token 보유를 의식적으로 수용. federation 축과 token 보유 축은 직교 — brokering 비용은 P1B/P2B 동일 학습 목표가 canonical OIDC+PKCE 흐름 관찰이면 P2B 수용. 대안: XSS 위협이 유의(운영·민감 데이터) → P1B/BFF 계열로 이동(토큰을 브라우저 밖으로). 저장 위치 선택은 raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff D1 raw/official-docs/oauth2-browser-based-apps-ietf-draft.md#OAUTH-BBA-C4 (BFF→TMB→Browser-based 는 보안 감소 순서, verbatim), #OAUTH-BBA-C3 (browser-based client 정의), raw/official-docs/owasp-html5-storage-xss-spa.md#OWASP-HTML5-C2 (단일 XSS 로 전량 탈취) official-standard + official-reference BFF 채택 여부는 raw/branch-notes/feature-keycloak-bff-vs-spa-direct D1 이 소유 — 본 결정은 그 판정을 P2B(federation 포함)로 확장 적용한 것이며, 확장의 타당성(federation 이 위협 모델을 바꾸지 않음)은 §Claims To Verify 대상

구현 가이드

본 브랜치는 코드 repo 가 없다 (학습용 문서·다이어그램 단계 — §Audit NO_CODE_REPO). 따라서 본 §는 코드 명세가 아니라 ① P2B 를 세우는 구성 순서(owner 브랜치 배선)② P2A 대비 zero-change 검증 지점 을 명세한다. 모든 항목 등급은 documented-only 또는 plannedactually-implemented 주장 없음. Out-of-scope 정제(R3): 개별 정책 detail(authenticator 토글·mapper 필드·Google client 등록 절차)은 owner 브랜치 소유이므로 본 §에 재진술하지 않는다 — 포인터만 둔다.

1. P2B 구성 순서 (owner 브랜치 배선)

Trace: D8 (Keycloak 레이어 brokering) 의 도출. 각 단계의 정책은 괄호 안 owner 브랜치가 소유하며 본 표는 순서와 의존만 명세한다.

  • UNSUPPORTED_IMPL_DECISION: 단계 순서 자체(1→5)는 어느 공식 문서도 규정하지 않는다. 사용자 trade-off — Google client 를 먼저 만들어야 Keycloak 에 넣을 client_id/secret 이 생기고(2→3), IdP 를 등록해야 redirect URI 확정값이 나오는 순환 의존이 있어, "Keycloak 에서 IdP 를 먼저 생성해 redirect URI 를 얻고 → Google 에 등록 → 되돌아와 secret 입력" 순으로 끊었다. 반대 순서도 가능하나 redirect URI 를 손으로 추측해야 해서 오타 위험이 크다.
  • UNSUPPORTED_DECISION (0단계의 근거 등급): CVE-2026-9087raw 에 아카이브된 출처가 없다 — 현재 근거는 sibling branch-note 2곳의 자기 보고뿐이며, CLAUDE.md §11 상 note→note 전이는 근거가 아니다. 따라서 0단계는 "확인하라"는 게이트일 뿐 버전 번호를 FACT 로 단정하지 않는다(형제 노트가 적은 26.3.0~26.6.1 / patched 2026-06-02 PR #49513 도 미검증 전언). 사용자 trade-off — 근거가 얇아도 계정 탈취 방어의 우회 라는 주장의 파급이 크므로, 검증 전까지 게이트를 열어두지 않고 닫아둔다(fail-safe). 해소: 공식 advisory 를 wiki-source-summarizer 로 아카이브 (§TODO).
# 단계 산출물 (다음 단계 입력) 정책 owner (Reference-Only) 등급
0 Keycloak 버전 하한 확인 — 본 노트가 consume 하는 D2 의 core 방어(Confirm Link + Verify Existing Account)가 우회되지 않는 patched 버전인지 먼저 확인. 미확인 상태로 1단계 진행 금지 버전이 고정된 스택 버전 caveat 의 출처는 raw/branch-notes/feature-keycloak-idp-brokering-google-client D6 의 Open Risk (CVE-2026-9087 — cross-session verification proof 가 upstream identity 에 미결속, trustEmail=false+email 인증 상태에서도 우회 존재로 보고). 형제 raw/branch-notes/feature-keycloak-account-linking-spa-ux 도 동일 항목을 flag needs-confirmation ⚠️
1 Keycloak realm 에 Google IdP 생성 → Redirect URI 확정값 확보 https://<kc-host>/realms/<realm>/broker/google/endpoint raw/branch-notes/feature-keycloak-idp-brokering-google-client D1 — Admin Console Identity ProvidersAdd providerGoogle planned
2 Google Cloud Console 에 OAuth Web application client 생성 + 1 의 Redirect URI 등록 client_id, client_secret 같은 노트 D2 (Web application client) · D3 (Redirect URI 그대로 복사, exact-match) planned
3 Keycloak IdP 에 client_id/client_secret 입력 + scope·discovery·trustEmail 설정 동작하는 broker 같은 노트 D4 (scope openid profile email 최소) · D5 (discoveryURL) · D6 (trustEmail=false) planned
4 First Broker Login Flow 정책 확정 (AutoLink 추가하지 않음) 사용자 매핑 정책 raw/branch-notes/feature-keycloak-first-broker-login-flow D1 · D2 · D3 planned
5 IdP Mapper 구성 (attribute importer + Sync Mode) Keycloak user attribute raw/branch-notes/feature-keycloak-google-claim-attribute-mapping D3 (IMPORT) · D4 (4종 mapper) planned

검증 종료 조건: SPA 로그인 화면에 "Sign in with Google" 버튼이 자동 노출되고(3h 이후), SPA 가 받은 token 의 iss 가 Keycloak realm URL 이면 P2B 성립.

2. P2A → P2B 변경량 (zero-change 검증 지점)

Trace: D8 · D9 의 도출 + §목표/WHY 의 핵심 통찰("SPA 코드 변경 거의 0")을 검증 가능한 형태로 고정. 자식 raw/branch-notes/feature-keycloak-federation-spa-zero-change D1 (idpHint 미사용) 이 SPA 측 결정을 소유.

  • UNSUPPORTED_IMPL_DECISION: "거의 0"의 정량 기준(LoC diff = 0 인지, button label 1줄인지)은 어떤 근거 문서도 규정하지 않는다. 사용자 trade-off — P2A 실 구현 부재로 diff 를 아직 못 잰다. §TODO 의 "실 LoC diff" 항목으로 이연하며, 그 전까지 "거의 0"은 주장이지 측정값이 아니다.
레이어 P2A P2B 기대 변경량 검증 방법
SPA keycloak-js 초기화 init({onLoad, pkceMethod:'S256'}) 동일 0 줄 P2A/P2B 설정 파일 diff — 차이 0 이어야 함
SPA 로그인 트리거 keycloak.login() 동일 (idpHint 미사용 → Keycloak 화면이 Google 버튼 노출) 0 줄 raw/branch-notes/feature-keycloak-federation-spa-zero-change D1
Backend JWT validation issuer-uri = Keycloak realm 동일 0 줄 #SSRS-JWT-C1 — issuer-uri 자동 검증
Backend audience 검증 aud = backend client 동일 0 줄 D7 위임
Keycloak admin 설정 realm + client + IdP + Mappers + FBL flow 변경 전량이 여기 집중 §1 표
Google Cloud Console 없음 + OAuth client 신규 §1 표 2단계

논증: 변경량이 전부 마지막 2행(admin/console)에 몰리고 코드 4행이 0 이면, "brokering 은 SPA 에 투명하다"는 D8 의 주장이 성립한다.

3. D5 (SPI 미사용) 의 구현 귀결

Trace: D5 (OWNED) 의 도출. #KC-BROKER-SPI-C1·#KC-BROKER-SPI-C2 는 SPI 의 존재와 용도를 규정할 뿐, 미사용 시 무엇을 하지 않아도 되는지는 규정하지 않으므로 아래는 그 대우(contrapositive).

  • UNSUPPORTED_IMPL_DECISION: 없음 — 아래는 "SPI 를 안 쓴다"의 직접 귀결이며 새 메커니즘 선택이 아니다.
항목 SPI 미사용 시 SPI 채택 시 (대안)
배포 산출물 Keycloak 컨테이너 이미지 그대로 (jar 추가 없음) provider jar 빌드 + providers/ 배치 + 재빌드
Keycloak 업그레이드 built-in provider 가 함께 유지보수됨 SPI 인터페이스 호환성 직접 추적
구성 방법 Admin Console 선언적 설정 (§1) Java 코드 + 컴파일

엣지·실패·의존

실패·엣지 경로

경로 기대 동작 소유/근거
사용자가 Google consent 거부 Google 이 error=access_denied 로 broker endpoint 회신 → Keycloak 로그인 화면 복귀 (SPA 는 code 를 못 받음). SPA 는 이 실패를 Keycloak 실패와 구분 못 함 — brokering 투명성의 대가 D8 의 귀결. 정확한 Keycloak 화면 동작은 needs-confirmation (§Claims To Verify)
Google email_verified=false 계정 AutoLink 없이 Confirm Link 소유증명 경로로 처리해 silent auto-link만 차단. 전체 link/생성 hard-reject는 현재 미구현 raw/branch-notes/feature-keycloak-first-broker-login-flow D4
같은 email 의 기존 local user 존재 Confirm Link 경로 (SMTP realm 은 email 검증이 기본) — 본 노트 원문의 "비밀번호 확인" 은 기본 아님 raw/branch-notes/feature-keycloak-first-broker-login-flow D2
Google-first 가입자(비밀번호 미설정)가 Re-authentication 요구받음 lockout 가능 — 재인증 수단 없음 owner Open Risk 승계 (같은 노트 D2)
personal Gmail (hd claim 부재) role 미부여 상태 통과 — 학습 단계에선 허용 raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role D3
Google client_secret 유출 Google client 재생성 + secret rotation. 환경별 client 분리로 폭발반경 축소 raw/branch-notes/feature-keycloak-idp-brokering-google-client D7
Google JWKS 키 rotation Keycloak 이 캐시 갱신 (Keycloak 책임, backend 무관) raw/branch-notes/feature-keycloak-three-leg-trust-chain D2 (UNSUPPORTED_DECISION 상태)
Keycloak 버전이 patch 이전 D2 가 위임한 core 방어(Confirm Link + Verify Existing Account)가 우회 가능 — 즉 본 노트가 "owner 가 막아준다"고 가정한 계정 탈취 경로가 실제로는 열려 있을 수 있음. 기대 동작: §구현 가이드 §1 0단계에서 차단(스택 기동 전) raw/branch-notes/feature-keycloak-idp-brokering-google-client D6 Open Risk (CVE-2026-9087). 근거 등급 needs-confirmation — raw 출처 부재, note→note 전언 (§구현 가이드 §1 UNSUPPORTED_DECISION)
SPA XSS access token 탈취 → 만료까지 유효 (즉시 revoke 불가). P2B 의 본질적 약점 D9 · #OWASP-HTML5-C2
Keycloak 다운 Google 로그인 포함 전 인증 경로 중단 — brokering 은 Keycloak 을 단일 장애점으로 만든다 (P2A 대비 축소 없음, 단 Google 의존이 추가돼도 Keycloak 없이는 무의미) D8 의 귀결 (Advisory)

다른 계약 의존

본 노트는 구성 허브이므로 의존이 많다. 각 owner 의 D-row 가 바뀌면 본 노트 §DEM 의 1줄 요약이 낡는다 → /sync 수거 대상.

  • raw/branch-notes/feature-keycloak-internal-spa-direct-no-google (P2A) D1 · D3 · D4 · D5 — 본 노트의 정의상 baseline (P2B ≡ P2A + brokering). §구현 가이드 §2 의 "P2A" 열 전량이 P2A 소유 결정이다: D5(PKCE S256 의무) = 1행 baseline, D3(backend = iss+signature+exp+aud 4종) = 3~4행 baseline, D1(P2A = SPA Direct 정의). 바뀌면: §2 의 zero-change 논증이 기준선째 바뀌고 D8·D9 의 전제가 흔들린다.
    • 2026-07-18 owner 정합 완료: zero-change invariant는 본 노트 D8이 소유하고 P2A D4는 이 행을 가리키는 Reference-Only pointer로 전환했다.
    • ⚠️ 미소유 관심사 (P2 sub-branch 지시): 인용 raw 가 본 노트류를 명시 지목한다 — raw/official-docs/oauth2-browser-based-apps-ietf-draft.md: P2(SPA-direct) 가 이 draft 의 §6.3.2 (PKCE MUST, CSRF 방어 MUST) 요건을 실제로 만족하는지 P2 sub-branch 에서 별도 검증. PKCE 는 P2A D5 가 소유하나 CSRF/state 방어는 어느 노트도 Decision 으로 소유하지 않는다(P2A 는 미체크 체크리스트 항목으로만 보유) → #OAUTH-BBA-C3 의 허용은 조건부이므로 D9 의 전제이기도 하다. 소유자 지정 필요 (§TODO).
  • raw/branch-notes/feature-keycloak-first-broker-login-flow D1 · D2 · D4 — 사용자 매핑/링크 정책 전량 consume. 바뀌면: §DEM D1·D2, §구현 가이드 §1 4단계, §엣지 표 3~4행 영향. ⚠️ 같은 노트가 realm SMTP 사실("학습 스택은 SMTP 없이 시작 → 자동 폴백")을 무라벨 단정으로 보유 → 본 노트 D10 과 이중 주장 (/sync 수거 대상, owner 는 D10 이어야 함 — owner 노트가 realm 사실을 자기 범위 밖으로 명시 배제했으므로).
  • raw/branch-notes/feature-keycloak-google-claim-attribute-mapping D3 · D4 · D5 — attribute 매핑·Sync Mode consume. 바뀌면: §DEM D3·D6, §claim mapping 표 정정 주석, §구현 가이드 §1 5단계 영향.
  • raw/branch-notes/feature-keycloak-idp-brokering-google-client D1D7 — Google client 등록·scope·trustEmail consume. 바뀌면: §구현 가이드 §1 13단계 전량 영향.
  • raw/branch-notes/feature-keycloak-account-linking-sub-vs-email D1 — link key = sub. 바뀌면: §claim mapping 표 sub 행 + D6 영향.
  • raw/branch-notes/feature-keycloak-three-leg-trust-chain D1 · D2 · D5 — 3-leg 검증 매트릭스 consume (자식). 바뀌면: §DEM D7, §신뢰 경계 영향.
  • raw/branch-notes/feature-keycloak-federation-spa-zero-change D1 — idpHint 미사용 (자식). 바뀌면: §구현 가이드 §2 의 "SPA 로그인 트리거 0 줄" 논증이 깨짐 (idpHint 를 쓰면 SPA 코드가 바뀜 → D8 의 zero-change 주장 약화).
  • raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff D1 · D2 — token 저장 위치. 바뀌면: D9 의 위협 모델 전제 영향.
  • raw/branch-notes/feature-keycloak-bff-vs-spa-direct D1 — SPA Direct 학습 1순위 채택. 바뀌면: D9 의 상위 전제가 무너짐 (BFF 로 이동 시 P2B 자체가 P1B 로 대체됨).
  • raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role D1/D3 — role mapper override와 hd 정책을 consume한다. IdP-level default IMPORT와 role mapper override FORCE는 적용 계층이 분리됐다.

검증해야 할 주장

P2B 는 문서/다이어그램 단계 (documented-only). 실 구현 시 검증해야 할 동작:

Claim Why uncertain How to verify Status
Keycloak 의 default first-broker-login flow 가 "Automatically Link" 위험 동작을 가짐 (변경 필요) (2026-07-17) 해소 — 주장이 틀렸음. #KC-FBLVERIFY-C4 의 공식 WARNING 상 AutoLink 는 기본값이 아니라 별도 opt-in dangerous authenticator. owner raw/branch-notes/feature-keycloak-first-broker-login-flow D1 이 정정 보유 (검증 불필요 — 전제 오류) 잔여 검증은 owner 의 Claims To Verify 로 이관: AutoLink authenticator 의 정확한 추가 위치 확인 resolved-corrected
Mapper Sync Mode = FORCE 시 Google 측 name/picture 변경이 다음 로그인 시 overwrite (2026-07-17) 위임. Sync Mode 는 본 노트 소유 아님 → owner raw/branch-notes/feature-keycloak-google-claim-attribute-mapping D3 (IMPORT) 이 #KC-SYNCMODE-C3/#KC-SYNCMODE-C4 로 정의 보유 owner 의 Claims To Verify 로 이관 (Admin UI 드롭다운 라벨 + 실동작 캡처) delegated
"Confirm Link Existing Account" authenticator 로 변경 시 사용자가 기존 비밀번호 입력 후에만 link 진행 (2026-07-17 갱신) SMTP 설정 realm 은 email 검증이 기본(#KC-FBLVERIFY-C1) → "비밀번호 입력"은 email authenticator 를 DISABLE 해야 관철됨(#KC-FBLVERIFY-C2). 본 노트 원문 전제가 부정확 flow copy 후 email authenticator DISABLE → 같은 email local user 의 비밀번호 prompt 확인. owner 소유raw/branch-notes/feature-keycloak-first-broker-login-flow D2 delegated
Google ID token 의 email_verified claim 이 항상 존재하고 boolean (Personal vs Workspace 차이 없음) GOOGLE-OIDC-C5nonce 만 verbatim. email_verified 자체의 always-present 보장은 본 인용에 없음 실 Personal Google account + Workspace account 두 가지로 federation → Keycloak Events 로그에서 claim 값 확인 needs-confirmation
Backend Spring Security 가 Keycloak access token 의 iss claim 으로 (Google 이 아닌) Keycloak realm URL 만 신뢰 SSRS-JWT-C1/C2issuer-uri 자동 검증을 보장. 그러나 Google token 이 우회 경로로 backend 에 도달 가능한지는 별도 위협 모델 Google ID token 을 직접 backend /api/me 에 제출 → 401 응답 확인 (issuer mismatch) planned
Advanced Claim to Role mapper 로 hd claim → role 매핑 시 personal account (no hd) 가 role 미부여 상태로 통과 KC-IDP-MAPPER-C4 는 mapper 종류 목록이 verbatim 부재. GOOGLE-OIDC-C7hd 부재 처리를 직접 다루지 않음 Personal Gmail 로 로그인 → Keycloak user 의 realm role / attribute 확인 needs-confirmation
XSS 시 SPA 의 access token 탈취 가능 (P1B 대비 P2B 의 본질적 약점) OWASP-HTML5-C1/C2/C3 는 storage XSS 위협을 보장. memory 보관 token 도 동일 위협인지는 추가 reasoning 필요 의도적 XSS payload 주입 (테스트 페이지) → document.cookie 또는 window.tokenStore 접근 가능성 확인 planned
(D9) federation 추가가 P2B 의 token 위협 모델을 바꾸지 않는다 (직교성) — 즉 #OAUTH-BBA-C4 의 보안 순서가 Google IdP 유무와 무관하게 성립 OAUTH-BBA-C4 는 세 패턴의 보안 순서를 규정하나 federation 유무를 변수로 다루지 않는다 — 직교 주장은 본 노트의 추론 P1B/P2B 의 위협 목록을 나란히 작성해 brokering 이 추가하는 위협(Google client_secret, 3-leg)이 token 보유 축과 독립임을 표로 대조 planned
(D8) Google consent 거부 시 SPA 가 받는 최종 상태 (Keycloak 로그인 화면 복귀 vs SPA 로 error redirect) brokering 투명성의 실패측 동작이 어느 인용에도 없음 Google consent 화면에서 "취소" → 브라우저 최종 URL + SPA 상태 관찰 needs-confirmation
(D9) P2B 가 IETF draft 의 Browser-based OAuth 2.0 Client 정의에 실제로 대응하는가 — D9 의 핵심 전제 #OAUTH-BBA-C3 의 정의(브라우저 앱 = public client, 모든 OAuth 책임을 브라우저에서, resource server 와 직접 통신)와 P2B 서술이 축자 일치하나, 근거 raw 의 "Does not prove" 가 P2B 포함 6조합의 3분류 1:1 대응을 부인 → 매핑은 본 노트의 판정이지 표준 확정이 아님 draft §6.3 정의와 P2B 구성을 요건별로 1:1 대조 (public client 여부 · client credentials 부재 · token 직접 보유 · RS 직접 호출). 추가로 §6.3.2 의 PKCE MUST / CSRF 방어 MUST 충족 여부 확인 — draft 가 "P2 sub-branch 에서 별도 검증"으로 본 노트류를 명시 지목 planned
(D9) P1B(oauth2-proxy / Traefik ForwardAuth) 가 IETF draft 의 BFF 정의에 실제로 대응하는가 — 이 매핑이 있어야 "P2B < P1B" 를 표준 권위로 말할 수 있음 근거 raw 가 명시적으로 부인: oauth2-browser-based-apps-ietf-draft.md "Does not prove" 열 — oauth2-proxy/ForwardAuth(P1A/P1B)가 draft 의 BFF 정의와 1:1 동일하다는 것 … 매핑 정합성은 별도 확인 필요. 본 노트의 추론 draft §6.1 의 BFF 요건(backend = confidential client · 토큰을 쿠키 세션에 보관 · 모든 요청을 프록시)을 oauth2-proxy 실동작과 1:1 대조. 프록시 요건이 어긋나면 P1B 는 BFF 가 아니라 §6.2 Token-Mediating Backend 이거나 그 외 → 순서 주장 재작성 필요 planned
(D8) Hide on Login Page 토글의 신규 IdP 등록 시 기본 상태가 OFF (=버튼 자동 노출) — D8 의 zero-change 메커니즘 전제 #KC-HIDELOGIN-C3 은 "ON 일 때만 미노출"을 규정할 뿐 기본값을 진술하지 않음. 기본이 ON 이면 SPA 가 kc_idp_hint 를 보내야 하므로 D8 의 "SPA 코드 0줄" 이 붕괴 Google IdP 신규 등록 직후 아무 설정도 건드리지 않은 상태로 로그인 화면 방문 → "Sign in with Google" 버튼 노출 여부 visual verify. 자식 raw/branch-notes/feature-keycloak-federation-spa-zero-change 가 동일 검증 소관 needs-confirmation
(D10) P2B 학습 realm 에 SMTP 가 실제로 미설정 — D2 의 fork 가 Re-authentication 으로 자동 폴백된다는 전제 부재 근거로 도출: raw/branch-notes/feature-keycloak-docker-compose-stack 이 SMTP/mail 을 일절 구성하지 않음에서 추론했을 뿐, "SMTP 없음"의 명시 선언은 없음 스택 기동 후 Admin Console → Realm settings → Email 탭이 비어 있는지 확인 + 같은 email local user 로 링크 시도 → 비밀번호 prompt(Re-auth) 가 뜨는지 관찰 (email 검증 메일이 오면 전제 붕괴) needs-confirmation
(0단계) CVE-2026-9087 의 실재·영향 버전·patch 버전 raw 출처 부재 — sibling branch-note 2곳의 자기 보고뿐이며 note→note 전이는 근거가 아님 (CLAUDE.md §11) 공식 advisory(NVD / Keycloak security advisory / 해당 PR)를 wiki-source-summarizerraw/official-docs/ 에 아카이브 → 영향 버전·patch 를 verbatim 확보 후 §구현 가이드 §1 0단계를 FACT 로 승격 needs-confirmation

Audit & Findings

2026-07-17 /branch-spec pass 의 감사 결과. 자동 수정하지 않고 기록만 한다 (rules/consistency-contract.md — "적용은 항상 승인 후", "owner 문서 우선").

ID 코드 위치 내용 조치
CONTRADICTION-1 CONTRADICTION §외부 근거 "비교 핵심" 마지막 문장 본 노트: "default Auto-Link은 보안 위험 → Confirm Link 로 변경". 공식(#KC-FBLVERIFY-C4) + owner raw/branch-notes/feature-keycloak-first-broker-login-flow D1: OOTB 기본이 이미 Confirm Link, AutoLink 는 별도 opt-in 해소됨 (2026-07-17, 사용자 판정 = 인라인 정정 마커) — 원문 verbatim 보존 + 정정 blockquote 추가. Claims To Verify 1행 resolved-corrected 로 갱신
CONTRADICTION-2 CONTRADICTION + DUAL_OWNERSHIP §claim mapping · raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role D1 IdP-level default와 role mapper override를 같은 설정으로 취급했던 drift 해소 (2026-07-18) — profile default IMPORT는 google-claim owner D3, role mapper override FORCE는 role owner D1로 적용 계층 분리. 본 hub는 pointer만 유지
RESTATEMENT-1 RESTATED_FOREIGN_DECISION §claim mapping · §신뢰 경계 foreign decision 상세 복제 해소 (2026-07-18) — 두 섹션과 delegated D-row를 owner pointer + 한 줄 불변식으로 축소
NO_CODE_REPO (본 pass 로컬 라벨) 전역 keycloak-patterns 프로젝트는 코드 repo 부재 — actually-implemented 확정 불가 (src/ grep 대상 없음). ca-tmpl 은 본 브랜치와 무관한 별개 프로젝트 §구현 가이드를 admin 구성 절차로 작성, 전 항목 documented-only/planned 유지
COVERAGE_EXEMPT (게이트 결과) frontmatter governing_docs 부재 + related_projects: [keycloak-patterns] (ca-* 아님) → rules/coverage-gate.md §7 에 의해 coverage 면제 조치 없음 (정상)

depth 게이트 1회차 findings (2026-07-17 branch-depth-auditor, Blocking 1

# 심각도 내용 해소 (2회차 반영)
1 R1 Blocking D8(OWNED)의 Supporting Claim 이 #KC-IDP-BROKER-C1 L0 1개뿐 — "IdP 를 붙이면 SPA 가 정말 안 바뀌나"를 되묻게 됨 해소#KC-HIDELOGIN-C2(IdP 구성 시 로그인 페이지에 옵션 자동 노출 = L1 메커니즘) · #KC-HIDELOGIN-C1 · #KC-HIDELOGIN-C3(ON 일 때만 미노출 + kc_idp_hint = L2 경계) · #KC-GIDP-C1 추가. 토글 기본 상태 caveat 는 Open Risk + Claims To Verify 로 이관
2 R1 Should-fix §장점/단점 이 "P2B < P1B 는 표준의 명시적 순서"라고 단정 — 그러나 #OAUTH-BBA-C4추상 3패턴 순서만 규정하고, 근거 raw 는 "oauth2-proxy/ForwardAuth ↔ BFF 매핑은 별도 확인 필요"라고 명시 부인. 노트가 "주관적 평가가 아니라"라고 써서 다음 독자의 검증을 차단 해소 — ①표준 확정(P2B ∈ Browser-based Client)과 ②본 노트 추론(P1B ∈ BFF)을 분리 기술 + Claims To Verify 1행 추가. 결론 방향은 ①만으로 유지됨
3 R2 Should-fix D2 가 owner 의 fork(Email vs Re-auth)를 옮겨오기만 하고 P2B 가 어느 쪽인지 미판정. owner 는 realm SMTP 사실을 자기 범위 밖으로 명시 배제 → 결정 변수의 주인이 없음 해소D10 신규(OWNED): 학습 realm = SMTP 미설정 → #KC-FBLVERIFY-C3 폴백으로 Re-auth 자동. D2 선택 조건이 D10 을 참조. 단 SMTP 미설정은 부재 근거 → Claims To Verify 1행
4 R4 Should-fix 버전 축 누락 — D2 의 core 방어가 CVE-2026-9087 로 우회 가능하다고 형제 2곳이 flag 하는데, 정작 배선 순서를 소유한 hub 에 버전 전제가 없음 (P2B 전문 버전 언급 0) 해소(조건부) — §구현 가이드 §1 에 0단계(버전 하한 확인) + §엣지 표 1행 추가. 단 CVE 는 raw 출처 부재(note→note 전언)UNSUPPORTED_DECISION 라벨 + 버전 번호 FACT 단정 회피 + advisory 아카이브 TODO(우선)
5 R1 Advisory §신뢰 경계 위임 고지가 owner(three-leg-trust-chain D5)의 UNSUPPORTED_DECISION 상태를 승계 표기하지 않음 — pointer 는 resolvable 하나 미지지 해소 — 고지에 "미지지 상태 승계" 경고 추가 (D7 셀이 이미 쓰던 패턴 적용)
6 R1 Advisory D5 의 SPI 트리거 기준("built-in 이 부족할 때")이 근거 raw 의 "증명하지 않는 것"과 정확히 일치 미조치 (수용) — 노트가 Open Risk 에 자가 flag 중이며 결정 자체는 #KC-BROKER-SPI-C1(L1)이 지지. 운영 전환 시 재평가

depth 게이트 2회차 findings (2026-07-17 재감사 — Verdict: Ready, Blocking 0

1회차 Blocking(D8 L0)은 실질 해소 확인 — #KC-HIDELOGIN-C2 의 verbatim 이 zero-change 메커니즘을 직접 진술(L1)하고 #KC-HIDELOGIN-C3 이 경계를 닫음(L2). 2회차 신규/잔여:

# 심각도 내용 해소
F1 R1 Should-fix 1회차 #2 의 fix 가 반대편으로 좁혀져 재발 — "①표준이 확정: P2B ∈ Browser-based Client" 라벨 자체가 over-claim. raw 의 Usage Boundaries 는 P2B 를 포함한 6조합 전부의 3분류 1:1 대응을 부인 해소 — ①을 "표준 확정 = 추상 3패턴의 순서만", ②P2B 매핑(강함·축자 일치), ③P1B 매핑(약함·raw 가 특정 부인)의 3단으로 분리. "P2B < P1B" 는 ②·③ 모두 필요하므로 단정 불가로 명시. D9 착수 판단은 ②만으로 유지됨을 논증 + P2B 매핑 Claims To Verify 1행 추가(P1B 행과 대칭)
F2 R4 Should-fix IMPLICIT_DEPENDENCY — 노트 정의가 "P2B = P2A + brokering" 이고 §구현 가이드 §2 의 P2A 열 전량이 P2A 소유인데 P2A 가 의존 목록에 없음(비교 링크뿐, D-ID 없음) 해소 — §다른 계약 의존 최상단에 P2A D1·D3·D4·D5 행 추가(D5=PKCE baseline, D3=backend 4종 검증 baseline). 부수 발견 2건도 기록: P2A D4 ↔ 본 노트 D8 의 zero-change 이중 주장(/sync), CSRF/state 방어 무소유(draft §6.3.2 MUST + raw 가 "P2 sub-branch 에서 별도 검증"으로 지목 → §TODO)
F3 R2 Should-fix D10 추가(1회차 #3 fix)의 부산물 — 소유 목록 2곳(§역할 blurb·§DEM 서두)이 "D5·D8·D9 "으로 남아 D10 이 외래 재진술로 오인·삭제될 위험. 배타적 열거라 단순 오타 이상 해소 — 2곳 모두 "D5 · D8 · D9 · D10" 으로 갱신
A1 R1 Advisory D10 의 "Re-auth 자동 폴백" 은 #KC-FBLVERIFY-C3 verbatim("Use this authenticator if the email authenticator is not available")이 관리자 지침문이지 런타임 서술이 아님 — 인용된 어느 claim 도 Re-auth 의 기본 등급을 진술 안 함 미조치 (수용) — 인용 출처 절이 "§Default first login flow authenticators" 이고 Claims To Verify 가 "비밀번호 prompt 관찰"로 이미 경험적 포착. 실 스택에서 해소
A2 R3 Advisory 0단계가 비교 임계값이 없어 자력 해제 불가한 게이트 — P2B 배선 전체가 외부 조사 TODO 에 종속 (결함 아닌 일정 리스크) 미조치 (수용) — 해제 경로(advisory 아카이브 TODO, 우선)가 이미 명시됨

마주친 문제

  • 아직 없음(문서 단계).

묶음 (자식 sub-sub-branches)

Sources는 상단 "외부 근거" 섹션. Errors/Interview/Lectures/Blog 는 현재 없음.

관련

관련 일일 노트

완료 후 정리

머지/종료 시점에 채움.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목:
    • locally-verified 항목:
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned):
    • 본 노트 전량 — 코드 repo 부재로 documented-only/planned (§Audit NO_CODE_REPO)