Files
llm-wiki/raw/branch-notes/feature-keycloak-federation-spa-zero-change.md
T

239 lines
24 KiB
Markdown

---
title: branch / feature-keycloak-federation-spa-zero-change (P2A → P2B 전환 시 SPA 코드 변경 없음 검증)
source_type: branch-note
status: raw
id: BR-KEYCLOAK-CHILD-26742876
kind: branch-child
project: keycloak-patterns-overview
work_item: WI-KEYCLOAK-PATTERNS-OVERVIEW-020
inherits: [DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1]
refines: []
overrides: []
depends_on: []
contract_packet: 1
branch: feature-keycloak-federation-spa-zero-change
parent_branch: feature-keycloak-patterns
related_projects: [keycloak-patterns]
tags: [branch, keycloak-patterns, spa, idp-brokering, google-federation, p2b]
created: 2026-05-25
target_merge:
status_label: in-progress
contract_packet_sha256: ad580e99833f810af4fd6be965901cc8462e0f8be77e4212fa2ff6fb00d2f59c
---
# branch: feature-keycloak-federation-spa-zero-change (P2A → P2B 전환 — SPA 코드 변경 없음 검증)
> Layer: `raw/branch-notes/` — [[raw/branch-notes/feature-keycloak-patterns]]의 branch-child.
> 학습 노트. P2B는 `documented-only` 단계.
<!-- section-id: branch-parent -->
## 부모 (필수)
[[raw/branch-notes/feature-keycloak-patterns]]
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: project governance hub가 AP1~AP4 taxonomy와 child progress index를 유지한다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | SPA code change 없이 IdP brokering을 추가하는 cross-cutting 변형으로 분류한다 | [[raw/project-notes/keycloak-patterns-overview]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
| D1 | SPA는 idpHint 없이 Keycloak login surface를 사용한다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
| D2 | Google OAuth redirect target은 Keycloak broker endpoint로 둔다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
없음.
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
**P2A 코드 그대로 두고 Keycloak에 Google IdP만 추가**해서 SPA가 변경 없이 Google 로그인 가능함을 실증한다. **IdP brokering의 핵심 가치**(= "SPA는 Keycloak만 안다") 검증.
면접 질문: "Google 로그인이 추가되면 SPA는 어디가 바뀌나요?"
→ "거의 0입니다. Keycloak 로그인 화면에 'Sign in with Google' 버튼이 자동으로 노출되고, SPA가 받는 token은 여전히 Keycloak이 서명한 JWT입니다. `issuer`는 Keycloak, `aud`는 backend client id, `azp`는 SPA client id입니다. backend Resource Server는 Google이 추가됐다는 사실 자체를 모릅니다."
- 이슈:
- PR:
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- 전 단계 (P2A) 검증 환경 가정 — [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] 완료 상태
- Keycloak admin에 Google Identity Provider 추가 절차
- SPA 로그인 버튼 / `keycloak-js` 초기화 코드 변경 0 확인
- Keycloak 로그인 화면이 "Sign in with Google" 버튼을 **자동으로** 노출하는지 확인
- SPA가 받는 Keycloak token이 P2A와 **동일 구조** (`iss`, `aud`, `azp`) 확인
### 제외 범위
- IdP Mappers 세부 — [[raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role]]
- 3-leg trust 검증 메커니즘 — [[raw/branch-notes/feature-keycloak-three-leg-trust-chain]]
- Account Linking 정책 — [[raw/branch-notes/feature-keycloak-account-linking-spa-ux]]
## 근거 (필수, 최소 1개+)
> 이 branch의 zero-change 검증·설정 결정의 **근거가 되는 외부 자료**. 같은 자료가 여러 결정의 근거면 여러 번 등장.
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/keycloak-identity-brokering-overview-official]] | D1 — Keycloak이 외부 IdP(Google)로 인증을 위임, social login=federation (KC-IDP-BROKER-C1); broker endpoint URL 포맷은 `needs-confirmation` (KC-IDP-BROKER-C2) |
| [[raw/official-docs/keycloak-google-idp-setup]] | D2 — Identity Providers → Add provider → Google 등록 절차 + Keycloak 표시 Redirect URI를 Google `Authorized redirect URIs`에 복사 (KC-GIDP-C1~C4), default scope `openid profile email` (KC-GIDP-C5) |
| [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] | D2 — Google redirect URI 검증 규칙: HTTPS 필수 / raw IP 금지 / exact match → `redirect_uri_mismatch` (GOOGLE-REDIR-C1~C3) |
| [[raw/official-docs/keycloak-securing-apps-overview-official]] | D1 — SPA는 표준 OIDC flow로 통합, adapter는 last resort (KC-SECAPP-C2) → `keycloak.login()` 호출부가 IdP 종류와 무관하게 불변인 컨텍스트 |
| [[raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official]] | D1 — "Hide on Login Page" 토글은 ON일 때만 provider를 로그인 페이지에서 숨긴다(KC-HIDELOGIN-C3), IdP 구성 시 로그인 옵션으로 나타나는 것이 기본 서술(KC-HIDELOGIN-C2)이고 realm의 IdP는 기본적으로 모든 애플리케이션에 활성화됨(KC-HIDELOGIN-C1) → "Sign in with Google 버튼 자동 노출" 근거 보강. 단 Hide 토글의 정확한 기본값은 결합 추론이며 원문이 직접 진술하지 않음(KC-HIDELOGIN-C3 Does not prove) |
| [[raw/official-docs/keycloak-idp-hint-client-suggested-official]] | D1 — **비교 대안(B)의 근거**: SPA가 특정 IdP를 강제하려면 `kc_idp_hint` 쿼리 파라미터(JS adapter는 `keycloak.createLoginUrl({ idpHint })`)가 필요함을 공식 확정 (KC-IDPHINT-C1, C3) — 이는 SPA 코드 변경에 해당하므로 zero-change 미채택. `keycloak.login({ idpHint })` 형태는 이 자료로 뒷받침되지 않음 (KC-IDPHINT-C3 Does not prove) |
| [[raw/official-docs/keycloak-identity-provider-redirector-default-idp-official]] | D1 — **비교 대안(C)의 근거**: SPA 코드 변경 없이 realm-level 로 특정 IdP 를 강제하는 `Identity Provider Redirector` / `Default Identity Provider` (KC-IDPREDIR-C1~C4). 단 로그인 선택 화면 자체를 제거(KC-IDPREDIR-C3)하고 realm 공유 client lockout 위험(KC-IDPREDIR-C1+C3 에서의 추론; C5 는 post-login flow 필요를 말함)이 있어, "사용자가 버튼 클릭" 흐름을 검증하는 본 branch 는 미채택 |
> zero-change의 **기준선(baseline)**은 외부 자료가 아니라 형제 브랜치 [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] (P2A) 의 토큰 구조·backend JWT 검증 계약이다 — §구현 가이드·§엣지·실패·의존에서 dependency로 참조.
## TODO
- [ ] 전제 확인: P2A 검증 환경 (SPA + Resource Server + Keycloak realm) 동작 — 등급: `planned`
- [ ] Google Cloud Console에서 OAuth 2.0 Client ID 생성 (redirect URI = `https://kc.example.com/realms/{r}/broker/google/endpoint`) — 등급: `documented-only`
- [ ] Keycloak admin → Identity Providers → "Add provider" → Google 선택 → client_id / client_secret 입력 — 등급: `documented-only`
- [ ] Keycloak 로그인 페이지 새로고침 → "Sign in with Google" 버튼 자동 노출 확인 — 등급: `documented-only`
- [ ] SPA 코드 (`keycloak-js` init, login button) **git diff = 0** 확인 — 등급: `documented-only`
- [ ] Google 로그인 성공 후 SPA가 받는 access_token decode → `iss=https://kc.example.com/realms/{r}`, `aud=<backend-client-id>`, `azp=<spa-client-id>` 확인 — 등급: `documented-only`
- [ ] backend Resource Server JWT validation 코드 **git diff = 0** 확인 — 등급: `documented-only`
## 진행 중 메모
- "SPA 코드 변경 없음"은 **로그인 버튼 라벨**도 안 바뀐다는 뜻. Keycloak 로그인 화면이 "Sign in with Google" 버튼을 제공하므로 SPA는 그저 `keycloak.login()`을 호출할 뿐.
- 만약 SPA가 자체 로그인 화면을 그리고 "Google로 로그인" 버튼을 직접 제공하려면 `keycloak.login({ idpHint: 'google' })`로 IdP를 강제할 수는 있음. 이건 코드 변경에 해당. 본 sub-branch는 **그것조차 안 한 경우**를 검증.
- access_token의 `iss`가 Keycloak이라는 사실이 **brokering의 본질**. Google ID token은 Keycloak 내부에서 소비되고 폐기됨 (또는 broker endpoint에 저장되지만 SPA가 받는 token에는 없음).
- 결과적으로 backend의 JWKS / issuer / audience validation 로직은 **P2A와 byte-for-byte 동일**.
## 결정 사항 (decisions)
- 2026-05-25: SPA가 **`idpHint`를 사용하지 않음.** 이유: brokering 가치 검증이 목적이므로 Keycloak 기본 로그인 화면이 IdP 선택을 노출하는 표준 흐름을 사용.
- 2026-05-25: Google Cloud OAuth Client는 **Web application** 타입 + redirect URI는 Keycloak broker endpoint 하나만 등록. SPA URL은 등록하지 않음 (SPA는 Google과 직접 통신하지 않음).
## 결정-근거 매핑
> 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시.
> `선택 조건` 열(R2): "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 `N/A`.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | SPA 는 `idpHint` 미사용 — Keycloak 기본 로그인 화면이 등록된 IdP(Google)를 버튼으로 자동 노출하는 표준 흐름 사용 | brokering 가치(= SPA는 Keycloak만 안다) 검증이 목표 → SPA는 `keycloak.login()`만 호출, IdP 선택은 Keycloak 로그인 화면에 위임(**zero-change**). **⟨대안 B⟩** SPA가 특정 IdP를 강제하려면 → `kc_idp_hint` 쿼리 파라미터(JS adapter 공식 예제는 `keycloak.createLoginUrl({ idpHint: 'google' })`; KC-IDPHINT-C1/C3) = **SPA 코드 변경**이므로 본 branch 범위 밖. **⟨대안 C⟩** realm-level `Identity Provider Redirector`의 Default Identity Provider(KC-IDPREDIR-C1/C2)로도 SPA 무관하게 강제 가능하나 **로그인 선택 화면 자체를 제거**(KC-IDPREDIR-C3) → 본 branch가 검증하려는 "사용자가 버튼 클릭" 흐름과 배치되어 미채택 | `raw/official-docs/keycloak-identity-brokering-overview-official.md#KC-IDP-BROKER-C1`, `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C1`, `raw/official-docs/keycloak-securing-apps-overview-official.md#KC-SECAPP-C2`, `raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official.md#KC-HIDELOGIN-C1`, `raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official.md#KC-HIDELOGIN-C2`, `raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official.md#KC-HIDELOGIN-C3`, `raw/official-docs/keycloak-idp-hint-client-suggested-official.md#KC-IDPHINT-C1`, `raw/official-docs/keycloak-idp-hint-client-suggested-official.md#KC-IDPHINT-C3` | `official-vendor-doc` | 대안 B(idpHint)는 이제 공식 인용 확보(KC-IDPHINT) — 종전 "인용 미확보" 위험 해소. 잔여 위험: **(1)** `Hide on Login Page` 토글의 **신규 IdP 생성 시 기본값(ON/OFF)** 을 원문이 직접 진술하지 않음 — KC-HIDELOGIN-C1(realm 기본 활성화)+C2(구성 시 로그인 옵션으로 나타남)의 결합 추론이며 C3은 ON 동작만 확정 → Admin UI 실측 필요(§Claims To Verify 1행). **(2)** note §목표·§진행 중 메모가 가정한 `keycloak.login({ idpHint })` 형태는 공식 예제(`createLoginUrl`)로 뒷받침되지 않음(KC-IDPHINT-C3 Does not prove) — keycloak-js adapter 레퍼런스 별도 확인. **(3)** 대안 C(realm-level Default IdP)는 같은 realm 공유 client(Admin Console 포함) lockout 위험 — 이는 KC-IDPREDIR-C1(로그인 폼 대신 IdP redirect)+C3(default IdP 못 찾으면 폼 표시)에서의 **추론**이며, KC-IDPREDIR-C5(NOTE: IdP 로그인 후 browser flow 미계속 → post-login flow 필요)는 lockout 을 직접 진술하지 않음. 본 branch 미채택, 참고만 |
| D2 | Google Cloud OAuth Client = Web application 타입, redirect URI = Keycloak broker endpoint 1개만 등록 (SPA URL 미등록) | SPA가 Google과 직접 통신하지 않고 Keycloak이 server-side broker → Google이 로그인 후 redirect하는 목적지는 Keycloak broker endpoint 뿐 → Web application 타입 + Keycloak이 표시하는 Redirect URI 1개만 등록. 반대로 SPA가 Keycloak을 우회해 Google에 **직접** OIDC를 하는 대안이면 SPA origin을 Google에 등록해야 하나, 그건 brokering 포기(= 본 패턴 아님) | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C3`, `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C4`, `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C1`, `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C2`, `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C3`, `raw/official-docs/keycloak-identity-brokering-overview-official.md#KC-IDP-BROKER-C2` | `official-vendor-doc + needs-confirmation` | broker endpoint URL 포맷 `/realms/{realm}/broker/{provider}/endpoint`의 verbatim 부재 (KC-IDP-BROKER-C2 = `needs-confirmation`; KC-GIDP-C3도 정확한 path 형식은 명시 없음 → Admin UI 자동 표시값을 신뢰원으로 사용) → 실 Admin UI 표시값 캡쳐로 확정 필요. "Web application" 클라이언트 타입 명칭은 Google Cloud Console UI 관행 — 위 인용은 타입명 자체를 verbatim 보장하지 않음 (§구현 가이드 `UNSUPPORTED_IMPL_DECISION`) |
## 구현 가이드
> 본 branch는 `documented-only` 학습 노트 — "구현"은 **① Keycloak/Google 설정 절차 + ② zero-change 검증 방법**의 사전 명세다. 각 sub-section은 §Decision Evidence Map의 `Decision ID` + `Supporting Claim ID`로 trace. 실제 코드 변경이 없는 검증이므로 대부분 `planned`/방법 명세이며, P2A 실 구현에 종속되는 detail은 그 종속을 명시한다.
### 1. Google IdP 등록 절차 (양방향 등록)
> **Trace**: D2 (KC-GIDP-C1~C4, GOOGLE-REDIR-C1~C3) + D1 진입점(KC-IDP-BROKER-C1). Google ↔ Keycloak 양쪽 등록이 서로의 입력.
>
> - **UNSUPPORTED_IMPL_DECISION**: "Web application" 클라이언트 타입 선택 — KC-GIDP/GOOGLE-REDIR 인용은 이 타입 명칭을 verbatim 보장하지 않음. trade-off: Keycloak broker는 client_secret을 보관하는 server-side confidential client이므로, Google Console의 SPA/Desktop/Mobile 타입이 아니라 **Web application** 타입이 관행(secret 발급 + redirect URI 등록이 가능한 유일 타입).
| # | 위치 | 작업 | 근거 |
|---|------|------|------|
| 1 | Google Cloud Console | OAuth 2.0 Client ID 생성, 타입 = **Web application** | KC-GIDP-C2 (Google에서 Client ID/Secret 발급) + UNSUPPORTED(타입 명칭) |
| 2 | Google Console `Authorized redirect URIs` | Keycloak broker endpoint 1개만 등록: `https://<kc-host>/realms/<realm>/broker/google/endpoint`**HTTPS 필수 · raw IP 금지 · exact match** | GOOGLE-REDIR-C1(HTTPS), C2(no raw IP), C3(exact match → `redirect_uri_mismatch`) |
| 3 | Keycloak Admin → Identity Providers | `Add provider` 드롭다운 → **Google** 선택 → Client ID / Client Secret 입력 | KC-GIDP-C1, KC-GIDP-C2 |
| 4 | Keycloak `Add Identity Provider` 페이지 | 페이지가 표시하는 **Redirect URI** 값을 복사 → 위 #2의 Google `Authorized redirect URIs`에 붙여넣기 (양방향 일치) | KC-GIDP-C3, KC-GIDP-C4 |
| 5 | (검증 anchor) | broker endpoint URL의 정확한 path는 Admin UI 표시값을 신뢰(코드/인용상 verbatim 부재) | KC-IDP-BROKER-C2 (`needs-confirmation`) |
### 2. backend zero-change 검증 방법
> **Trace**: D1 (KC-IDP-BROKER-C1, KC-SECAPP-C2) + §목표(git diff = 0). 검증 대상은 "코드가 안 바뀐다"는 사실이므로 산출물은 diff 명령 결과와 token decode 대조표.
>
> - **UNSUPPORTED_IMPL_DECISION**: 검증 대상 파일의 정확한 경로·`keycloak-js` 버전은 P2A 실 구현에 종속 — P2A가 아직 `documented-only`이므로 경로를 확정할 수 없음(`planned`). trade-off: 파일 경로 대신 "IdP 종류와 무관한 호출부"(로그인 트리거·JWKS/issuer/audience validator)를 대상으로 정의.
| 검증 항목 | 방법 | 기대 결과 | 근거 |
|---|---|---|---|
| SPA 로그인 진입부 불변 | `keycloak-js` init + login 트리거 파일 `git diff` (P2A 대비) | diff = 0 (idpHint 미사용 → `keycloak.login()` 인자 불변) | D1; KC-SECAPP-C2(표준 flow) |
| backend JWT 검증 불변 | Resource Server validator 코드 `git diff` | diff = 0 (backend는 Google 추가를 모름) | [[raw/branch-notes/feature-keycloak-spring-rs-audience-validator]] D1 — backend validation/audience policy |
| token 구조 동일 | Google 로그인으로 받은 access_token decode → P2A 로컬 로그인 token과 claim 대조 | `iss`=`https://<kc>/realms/<realm>`, `aud`=`backend-client-id`, `azp`=`spa-client-id`**구조 동일** | [[raw/branch-notes/feature-keycloak-spring-rs-audience-validator]] D4 — audience owner; D1 |
> **주의(§엣지에서 상술)**: 구조(`iss`/`aud`/`azp`)는 동일하나 IdP Mapper가 role/group claim을 추가하면 **payload claim set은 커질 수 있음** — "byte-for-byte 동일"은 mapper 미적용 전제. Mapper 영향은 [[raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role]]로 위임.
## 엣지·실패·의존
> R4 캡처용. zero-change 검증 중 실제로 부딪힐 실패/엣지 + 다른 branch 계약 의존.
- **실패·엣지 경로**:
- **`redirect_uri_mismatch`**: Keycloak broker endpoint URL과 Google에 등록한 URI가 trailing slash/host/port까지 정확히 일치하지 않으면 Google이 거부. `KC_HOSTNAME` 오설정 시 Keycloak이 표시하는 endpoint URL이 어긋나 발생. 기대 동작: 로그인 실패 + Google `redirect_uri_mismatch`. (GOOGLE-REDIR-C3)
- **"Sign in with Google" 버튼 미노출**: Google IdP는 등록됐으나 로그인 화면에 버튼이 안 뜨는 경우 — realm mismatch 또는 IdP의 "Hide on Login Page" 옵션이 ON(공식 근거 확보: `raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official.md#KC-HIDELOGIN-C3` — ON일 때만 미노출). 단, 신규 IdP 등록 시 이 토글의 **기본 상태**는 원문이 직접 진술하지 않아(KC-HIDELOGIN-C3 Does not prove) 여전히 `needs-confirmation`. 이 경우 zero-change 전제(=화면이 버튼을 자동 제공)가 붕괴 → visual verify 필수(§Claims To Verify 1행).
- **token claim set 확대**: 구조(`iss`/`aud`/`azp`)는 불변이나, IdP Mapper로 role/group을 주입하면 access_token payload가 P2A보다 커짐. "byte-for-byte 동일"은 **mapper 미적용 전제**에서만 성립. 기대 동작: 구조는 검증 통과하되 claim set 차이는 별도 인지.
- **First Broker Login 충돌**: 같은 email의 기존 local user가 있으면 자동 link/충돌 분기 발생 — **본 branch 범위 밖**(zero-change 검증에 영향은 없으나 로그인 자체가 막힐 수 있음). 위임: [[raw/branch-notes/feature-keycloak-account-linking-spa-ux]].
- **다른 계약 의존**:
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] (P2A) 의 baseline 환경(SPA public client + PKCE S256 + Resource Server) 완료 **전제**. Backend 기준선은 [[raw/branch-notes/feature-keycloak-spring-rs-audience-validator]] D1 — backend validation/audience policy를 직접 따른다. 이 owner 계약이 바뀌면 "zero-change" 기준선 자체가 바뀐다.
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] (P2B, parent) 의 Google IdP 등록·Mapper·First Broker Login 상위 결정 — 특히 P2B `D6`(claim mapping)·`D7`(backend Keycloak-only 검증 / 3-leg trust)·`D1`/`D2`(First Broker Login 정책) — 에 의존. 본 branch는 그중 "SPA/backend 코드 변경 0" 축만 검증(나머지는 sibling으로 위임).
- **(2026-07-17 갱신)** 위 P2B `D1`·`D2`·`D6`·`D7` 은 주제는 그대로이나 **P2B 가 더 이상 직접 소유하지 않는다** — P2B 는 구성 허브로 정리되며 각각 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4·D2, [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4, [[raw/branch-notes/feature-keycloak-three-leg-trust-chain]] D1 로 위임됐다. 정책 **권위는 owner 노트**에 있으므로, 본 branch 의 전제가 바뀌었는지 확인할 때는 P2B 가 아니라 owner 를 본다.
- [[raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role]] — claim mapping이 token claim set에 미치는 영향(위 엣지 3행) 소유.
- [[raw/branch-notes/feature-keycloak-three-leg-trust-chain]] — Browser↔Keycloak↔Google 3-leg 검증 메커니즘 소유.
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| Keycloak 로그인 화면이 "Sign in with Google" 버튼을 자동으로 노출 (별도 SPA 코드 변경 없이) | raw 인용 KC-IDP-BROKER-C1 은 "delegate authentication" 까지만 보장. KC-HIDELOGIN-C1(realm 기본 활성화)+KC-HIDELOGIN-C2(구성 시 로그인 옵션으로 나타남)+KC-HIDELOGIN-C3(Hide 토글 ON일 때만 미노출)로 정황 근거는 보강됐으나, Hide 토글의 **신규 IdP 생성 시 기본값**은 원문이 직접 진술하지 않아(결합 추론) 실제 로그인 화면 UI 자동 노출은 여전히 별도 검증 필요 | P2A 환경에 Google IdP 추가 후 로그인 페이지 새로고침 → 버튼 노출 visual verify | `documented-only` |
| SPA 가 받는 access_token 의 `iss=https://kc.example.com/realms/{r}`, `aud=<backend-client-id>`, `azp=<spa-client-id>` 구조가 P2A와 동일 | 정확한 audience provisioning은 [[raw/branch-notes/feature-keycloak-spring-rs-audience-validator]] D4 소관이며 아직 runtime token을 발급하지 않음 | Google 로그인 성공 후 JWT decode + P2A 토큰과 claim-by-claim diff | `needs-confirmation` |
| backend Resource Server JWT validation 코드 git diff = 0 | 추론 (brokering 의 본질) — verbatim 보장 부재 | git diff 명령 실행 후 결과 확인 | `documented-only` |
| `keycloak-js` init / login button SPA 코드 git diff = 0 | 추론 — verbatim 보장 부재 | git diff 명령 실행 후 결과 확인 | `documented-only` |
| Google ID token 이 Keycloak 내부에서 소비되고 SPA 에 노출되지 않음 | KC-IDP-BROKER-C1 의 "delegate" 만 보장, token 격리는 별도 | network tap 또는 SPA 측 token 검사 | `needs-confirmation` |
| Keycloak broker endpoint URL 포맷 (`/realms/{realm}/broker/{provider}/endpoint`) 정확 | `KC-IDP-BROKER-C2` 자체가 `needs-confirmation` (Admin UI 관행, verbatim 부재) | Keycloak Admin UI → Identity Provider → Redirect URI 표시값 직접 캡쳐 | `needs-confirmation` |
## 마주친 문제
- (학습 단계, 미실행)
## 묶음
<!-- GENERATED: sources:start -->
- [[raw/official-docs/keycloak-identity-provider-redirector-default-idp-official]]
- [[raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official]]
- [[raw/official-docs/keycloak-idp-hint-client-suggested-official]]
<!-- GENERATED: sources:end -->
> 본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
> 근거 외부 자료(official-docs)는 상단 `## Sources / 근거` 표에서 관리 — Cluster 에는 중복 나열하지 않는다.
### 오류 기록 (이 sub-sub-branch 작업 중 발생)
- (없음 — 현재 documented-only 단계)
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase 3 실 구현 단계에 누적)
## 관련 일일 노트
## 완료 후 정리
> 학습 노트. P2B는 `documented-only` 유지. 실제 brokering 검증 환경 구축은 별도 마일스톤.
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경: 학습 노트 (`documented-only`)
- **wiki 추출 대상**:
- `actually-implemented` 항목: (없음)
- `locally-verified` 항목: (없음)
- `prod-verified` 항목: (없음)
- **추출하지 않을 항목**: 전 항목 (`documented-only`)