Files
llm-wiki/raw/branch-notes/feature-keycloak-idp-brokering-google-client.md
T

308 lines
34 KiB
Markdown

---
title: branch / feature-keycloak-idp-brokering-google-client (Keycloak IdP brokering — Google client 등록)
source_type: branch-note
status: raw
id: BR-KEYCLOAK-PATTERNS-OVERVIEW-015
kind: project-work-item
project: keycloak-patterns-overview
work_item: WI-KEYCLOAK-PATTERNS-OVERVIEW-015
inherits: [DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1]
refines: []
overrides: []
depends_on: [WI-KEYCLOAK-PATTERNS-OVERVIEW-003]
contract_packet: 1
branch: feature-keycloak-idp-brokering-google-client
parent_branch:
related_projects: [keycloak-patterns]
tags: [branch, keycloak-patterns, p1b, idp-brokering, google-oidc, oauth-client]
created: 2026-05-25
target_merge:
status_label: in-progress
contract_packet_sha256: d51e0ff4b2d65e1ea7a2b7023c4bca6352d3d0351991bf7928027836c6a8741f
---
# branch: feature-keycloak-idp-brokering-google-client (Keycloak IdP brokering — Google client 등록)
> Layer: `raw/branch-notes/` — [[raw/project-notes/keycloak-patterns-overview]]의 `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` 직접 branch.
> 학습 노트: P1B는 `documented-only` (실 구현 안 함).
> `status_label`: `in-progress`
<!-- section-id: branch-parent -->
## 부모 (필수)
[[raw/project-notes/keycloak-patterns-overview]]
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | Google OAuth client와 Keycloak Identity Provider 연결에 적용한다 | [[raw/project-notes/keycloak-patterns-overview]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
없음.
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
Keycloak이 Google을 외부 IdP로 broker하려면 두 단계 설정이 선행돼야 한다.
1. **Google Cloud Console**에서 OAuth 2.0 client 생성 (`client_id`, `client_secret`, redirect URI 등록).
2. **Keycloak Admin Console**의 `Identity Providers`에 Google provider 등록 (discovery URL + client credential 입력).
본 노트는 이 두 단계 설정 항목과 함정(특히 `trustEmail`)을 정리한다.
**핵심 통찰:**
- Google OAuth client는 **redirect URI를 정확히 일치시켜야** 함 — Keycloak broker callback URL (`https://<kc-host>/realms/<realm>/broker/google/endpoint`)
- `trustEmail = false`를 **명시 설정**하는 것이 채택값이다. default 값 자체는 미확정이다. 이 값은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4의 silent auto-link 방어에 defense-in-depth로 결합한다.
- Keycloak은 `discoveryURL` 한 줄만 입력하면 Google `authorize` / `token` / `userinfo` / `jwks_uri` endpoint를 자동 발견 (OIDC discovery 표준).
> ⚠️ **2026-07-16 `/branch-spec` 조사 정정 (원 프레이징 verbatim 보존, 정정만 surface)**: 위 "`trustEmail = false`가 **기본값**이자 권장값" 중 *권장값* 부분은 이번 조사로 근거 확보([[raw/official-docs/keycloak-identity-provider-trust-email-official]] — Google 같은 self-service IdP 에선 Keycloak 자체 email 검증을 우회하지 않는 `false` 가 안전, D6). 그러나 *기본값* 부분은 **공식 문서가 default 를 명시하지 않아 미확정**(`needs-confirmation`) — Keycloak Admin UI 신규 IdP 생성 폼 캡처로만 확정 가능. 상세는 §Decision Evidence Map D6 Open Risk + §Audit & Findings.
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- Keycloak Admin Console 의 Google IdP **등록 경로 + credential 입력** (D1·D2) — nav path, `Client ID`/`Client Secret`.
- **`discoveryURL` 로 OIDC endpoint 자동 발견** (D5) — 본 branch 고유 owned(형제 redirect-uri-policy 미포함).
- **`trustEmail` 값 정책** (D6) — 본 branch **core owned**. 형제 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 + [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] Scenario C 가 이 값을 defense-in-depth 로 consume(역방향 의존).
- scope `openid profile email` 최소권한 정책 (D4).
- Google Cloud Console OAuth 2.0 Web application client **등록 필드**의 요지 (D2·D3) — 단 redirect URI exact-match 규칙·byte-level 정의·다환경 URI·consent-screen verification 상세는 형제 redirect-uri-policy 로 **위임**(§구현 가이드 §3, §엣지·실패·의존).
- **환경별 OAuth client/project 분리 + credential never-commit 정책** (D7) — project 분리는 조건부, credential 보안은 무조건부(§D7 Open Risk).
### 제외 범위
> 의도적으로 제외한 것. 면접 등에서 "이건 범위에 없었습니다"라고 답할 근거.
- **실제 Google Cloud project 생성 / OAuth client 등록 / Keycloak 실 구성** — 본 sub-sub-branch 는 `documented-only` 학습 노트(코드 repo `/home/donghyeon/workspace/keycloak-patterns/` 미생성, §Audit `NO_GROUND_TRUTH`).
- **redirect URI exact-match 규칙 / byte-level 정의 / URL 변경 시 갱신 절차 / consent-screen verification 심사** → [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] (deep owner, GOOGLE-REDIR 계열 근거 5개 보유).
- **First Broker Login Flow authenticator *구성*(Confirm Link / Verify / AutoLink step)** → [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] (owner; 본 branch D6=`trustEmail` 을 defense-in-depth 로 consume).
- **Account linking primary key (`sub` vs email) 선택** → [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1.
- **Google claim → attribute mapper 구성 / Sync Mode 값 선택** → [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] (본 D6 의 FORCE 재평가 상호작용이 그 Sync Mode 값에 의존).
- **`email_verified=false` hard-reject 전용 custom SPI authenticator** → hub §5 Deferred(server-side SPI 트랙).
- 비-Google IdP / SAML federation.
## 근거 (필수, 최소 1개+)
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Google IdP 등록 공식 절차
- [[raw/official-docs/google-openid-connect-oidc]] — Google OIDC discovery / scope / claim 표준
- [[raw/official-docs/keycloak-identity-brokering-overview-official]] — Keycloak Identity Broker 공식 개요
- [[raw/official-docs/keycloak-identity-provider-trust-email-official]] — `Trust Email` 필드 공식 의미(ON 시 realm email 검증 skip, `email_verified` claim 기반 (un)marking, Sync Mode `FORCE` 상호작용). D6 (`trustEmail=false` 유지) 의 verbatim 근거 — 단 default 값은 이 자료로 증명 안 됨(`needs-confirmation` 잔존)
- [[raw/official-docs/google-oauth2-policies-environment-separation-official]] — Google OAuth 2.0 Policies 공식 문서. D7 (환경별 OAuth client/project 분리 + credential never-commit 규칙) 의 verbatim 근거 — 단 project 분리 의무는 "production" app 정의 충족 시에만 조건부 적용(현재 개인 학습 단계에는 미적용 가능성, 상세는 해당 raw 의 Usage Boundaries 참조)
## TODO
각 항목 옆에 증거 등급. 본 P1B sub-sub-branch는 전체 `documented-only` / `planned`.
- [ ] Google Cloud Console에서 OAuth 2.0 Client ID 생성 절차 정리 — 등급: `planned`
- Application type: `Web application`
- Authorized JavaScript origins: [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] D6 — server-side brokering에서는 비움
- Authorized redirect URIs: `https://<kc-host>/realms/<realm>/broker/google/endpoint`
- [ ] Keycloak Admin Console에서 Google IdP 등록 절차 정리 — 등급: `planned`
- `Identity Providers``Add provider``Google`
- `Client ID` / `Client Secret`: Google Console에서 발급한 값
- `Default Scopes`: `openid profile email` (Google OIDC 표준)
- [ ] `discoveryURL`로 OIDC endpoint 자동 발견 검증 — 등급: `planned`
- `https://accounts.google.com/.well-known/openid-configuration`
- 자동 발견 시 endpoint 수동 입력 불필요 (`authorize_endpoint`, `token_endpoint`, `userinfo_endpoint`, `jwks_uri` 자동 채움)
- [ ] `trustEmail` 옵션 정책 결정 — 등급: `documented-only`
- 기본값 `false` 유지 (보안 위험 회피)
- Google이 `email_verified=true` claim 제공 시에만 Keycloak이 email verified로 인정
- [ ] `Display Name on Login Page` 설정 — 등급: `planned`
- 사용자에게 보이는 버튼 라벨 (예: "Sign in with Google", "Google로 로그인")
- [ ] 환경별 OAuth client 분리 정책 정리 — 등급: `documented-only`
- dev / staging / prod 환경별 별도 OAuth client → redirect URI 충돌 방지
- Google Cloud Console의 client당 redirect URI 등록은 1:1 일치 필요
## 진행 중 메모
- Google OAuth client 생성 시 OAuth consent screen 설정도 필요 (앱 이름, 로고, scope 목록). 내부 사용자만 대상이면 `Internal` (Google Workspace 도메인) / 외부 공개면 `External` + verification 절차.
- discovery URL이 동작하면 Keycloak admin UI에서 endpoint 입력 필드가 read-only로 회색 처리되는 것을 확인 (Keycloak 25.x).
- redirect URI mismatch는 가장 흔한 함정 — Google Console 등록값과 Keycloak broker endpoint URL이 정확히 같아야 함 (trailing slash, scheme 포함).
- `Sync Mode` 옵션 (`IMPORT`, `LEGACY`, `FORCE`)은 매핑 단계에서 다룸 → [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]].
## 결정 사항 (decisions)
> 본 sub-sub-branch는 `documented-only` — 실 환경 결정 없음. **만약 구현한다면** 권장 기본값.
- 2026-05-25: `trustEmail = false` 유지 (보안 위험 회피). Google `email_verified=true` claim에만 의존.
- 2026-05-25: `discoveryURL` 사용 (수동 endpoint 입력 대신). Google이 endpoint URL 변경 시 자동 대응.
- 2026-05-25: 환경별 OAuth client 분리 (dev/staging/prod). 단일 client 공유 시 redirect URI 충돌 + secret 노출 범위 확대 위험.
- 2026-05-25: scope `openid profile email`만 요청 (최소 권한). 추가 scope 요청 시 Google OAuth verification 트리거 가능.
## 결정-근거 매핑
> 본 sub-sub-branch 는 `documented-only`. cited raw sources: `keycloak-google-idp-setup`, `google-openid-connect-oidc`, `keycloak-identity-brokering-overview-official`, `keycloak-identity-provider-trust-email-official`(D6), `google-oauth2-policies-environment-separation-official`(D7).
>
> `선택 조건` 열(R2): "이 조건일 때 이 결정, 다른 조건이면 어떤 대안". 분기 없으면 `N/A`.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | Keycloak Admin Console 에서 Google IdP 등록 시 `Identity Providers``Add provider``Google` 경로 사용 | Keycloak 내장 Google social provider 사용 시 항상 이 경로. 대안: generic `OpenID Connect v1.0` provider (목록에 없는 IdP 또는 커스텀 endpoint 를 직접 지정해야 할 때) | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C1` ("go to the `Identity Providers` left menu item and select `Google` from the `Add provider` drop down list") | `official-vendor-doc` | Keycloak admin console v2 (Keycloak 19+) 의 동일 경로 navigation 검증 필요 |
| D2 | Google Cloud Console 에서 OAuth 2.0 Web application client 생성, `Client ID` + `Client Secret` 발급 후 Keycloak 에 입력 | Keycloak 이 server-to-server 로 Google `/token` 호출(confidential client, secret 보관) → 항상 **Web application** type. 대안(Android/iOS/Desktop/limited-input client type)은 그 플랫폼에서 직접 도는 OAuth client 일 때만 (`GOOGLE-OAUTHPOLICY-C4` platform 별 분리) | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C2` ("you'll need to obtain a `Client ID` and `Client Secret` from Google") | `official-vendor-doc` | Google Cloud Console UI 의 정확한 OAuth consent screen 설정 절차는 본 인용 범위 밖 |
| D3 | Authorized redirect URIs 에 `https://<kc-host>/realms/<realm>/broker/google/endpoint` 등록 — Keycloak 측 발급값 그대로 복사 | 항상 Keycloak 이 표시하는 Redirect URI 를 그대로 복사(Google exact-match 요구, 분기 없음 = N/A). exact-match 규칙·다환경 URI·byte-level 정의는 형제 [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] D1~D4 가 deep owner (본 D3 는 등록 step 만, §Audit `SINGLE_OWNER_TENSION`) | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C3` ("One piece of data you'll need from this page is the `Redirect URI`") + `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C4` ("You'll also need to copy and paste the `Redirect URI` from the Keycloak `Add Identity Provider` page into the `Authorized redirect URIs` field") | `official-vendor-doc` | redirect URI 정확한 path 형식 (`/realms/<realm>/broker/google/endpoint`) 의 vendor verbatim 부재 — admin UI 자동 표시값 신뢰 |
| D4 | scope `openid profile email` 만 요청 (최소 권한) | 인증·식별만 필요(학습) → `openid profile email` 최소 scope. Google API(Gmail/Drive 등) 접근이 필요할 때만 scope 확장 → 단 sensitive/restricted scope 는 Google verification 심사 유발 | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C5` ("By default, Keycloak uses the following scopes: `openid` `profile` `email`") + `raw/official-docs/google-openid-connect-oidc.md#GOIDC-C4` (`email` claim 은 `email` scope 포함 시에만 제공) | `official-vendor-doc` | Google OAuth verification 트리거의 정확한 조건 (sensitive scope 목록) 은 cited raw 에 verbatim 없음 |
| D5 | `discoveryURL` 사용 (수동 endpoint 입력 대신) — `https://accounts.google.com/.well-known/openid-configuration` | IdP 가 well-known OIDC discovery config 를 게시(Google=제공) → discoveryURL 로 endpoint 자동 발견. 대안(수동 endpoint 4개 입력)은 discovery 미제공 IdP 또는 endpoint 를 명시적으로 pin/override 해야 할 때만 | `raw/official-docs/google-openid-connect-oidc.md#GOIDC-C5` ("The Discovery document for Google's OpenID Connect service may be retrieved from: `https://accounts.google.com/.well-known/openid-configuration`") | `official-vendor-doc` | Keycloak admin UI 가 discovery URL 입력 시 endpoint 필드를 자동 채우는지의 verbatim 인용 없음 — UI 캡처 검증 필요 |
| D6 | `trustEmail = false` 유지 — Google 같은 self-service 소비자 IdP 의 email 을 무조건 verified 로 신뢰하지 않고 Keycloak/flow 의 자체 검증을 유지 | IdP 가 Google 같은 **self-service 소비자 OAuth**(자기신고 email 존재) → `false`(Keycloak 자체 verification 유지). `true` 대안은 조직이 완전 통제하는 **enterprise SSO**(email_verified 구조적으로 항상 신뢰)에서만 — 본 Google 시나리오엔 부적합. 조건부 신뢰(email_verified 일 때만 skip)는 Keycloak **공식 미지원**(GitHub #8622 미병합) → custom SPI 필요, 학습 범위 밖 | `raw/official-docs/keycloak-identity-provider-trust-email-official.md#KC-TRUSTEMAIL-C1` (ON=realm email 검증 skip), `#KC-TRUSTEMAIL-C2` (`email_verified` 기반 (un)marking), `#KC-TRUSTEMAIL-C3` (Sync Mode `FORCE` 시 매 로그인 재평가) — 값 선택(=OFF)의 의미론적 근거 확정 | `official-vendor-doc` (값 선택 근거) — 단 **`default=false` 서브클레임은 `needs-confirmation`** | **공식 문서가 `trustEmail` 의 default 값을 명시하지 않음** — "false 가 기본값"은 미확정, Keycloak Admin UI(25.x/26.x) 신규 IdP 폼 캡처로만 확정(Claims To Verify). 값이 안전하다는 결론은 First Broker Login Confirm Link flow 무결성에 의존하나, **CVE-2026-9087**(cross-session verification proof not bound to upstream identity; 26.3.0~26.6.1 + main, patched 2026-06-02 PR #49513)은 `trustEmail=false`+email 인증 상태에서도 우회가 있었음을 보임 → 형제 first-broker-login-flow core 방어에 버전 caveat 필요(§Audit `CVE_CROSS_BRANCH`). `true` 의 (un)marking 재평가는 community Issue #39885(FORCE 버그, 비공식)로 신뢰도 낮음 — 근거 인용 금지 |
| D7 | 환경별 OAuth client/project 분리 (dev/staging/prod) — 단일 client 공유 시 redirect URI 충돌 + secret 노출 확대. **credential 은 public repo 에 절대 커밋 금지, secret manager 취급** | 현재(`documented-only`·실사용자 0) → **단일 client + 다중 redirect URI**(운영 부담 최소, blast-radius 우려는 실사용자 부재로 공허). Google "production"(C2) 기준 충족(실배포 tier·실사용자 >100 or 공개) → **환경별 별도 project**(C1 의무 발동, client 자동 분리). 과도기(팀원 dev 접근) → **별도 client(같은 project)**. credential never-commit(C3)은 단계 무관 **항상** | `raw/official-docs/google-oauth2-policies-environment-separation-official.md#GOOGLE-OAUTHPOLICY-C1` ("you must create separate projects in the Google Cloud Console for each deployment tier, such as development, staging, and production") + `#GOOGLE-OAUTHPOLICY-C3` ("You must never commit client credentials into publicly available code repositories") | `official-vendor-doc` (조건부 — 아래 Open Risk 참조) | **환경별 project 분리(C1) 는 Google 이 정의하는 "production" app 요건(`#GOOGLE-OAUTHPOLICY-C2`: 공유 안 함 또는 100명 미만 개인적으로 아는 사람 → personal use 로 예외) 충족 시에만 의무.** 본 branch 는 현재 `documented-only` 개인 학습 프로젝트라 이 조건을 충족하지 못해 project 분리가 "지금 당장의 공식 의무"는 아님 — 실 배포/실사용자 확대 시점부터 발동되는 **선제적 설계 근거**로만 인용. 반면 credential never-commit(C3) 은 production 스코프 밖 규정이라 지금부터 무조건 적용 |
## 구현 가이드
> *결정(Decisions)* 이 "*무엇*" 이라면 본 §는 "*어디에 어떻게*" 의 사전 명세. 본 branch 는 `documented-only` 학습 노트이므로 anchor 는 **공식 문서가 규정하는 필드·값**이고, 코드/Admin UI 로만 확인되는 것은 `planned`/`UNSUPPORTED_IMPL_DECISION` 로 표기한다(코드 repo `/home/donghyeon/workspace/keycloak-patterns/` 미생성, §Audit `NO_GROUND_TRUTH`).
> 본 branch owned 구현 대상은 **Keycloak Admin 측 Google IdP 등록(D1·D2·D5·D6)** 이 핵심이고, Google Console 측(D2·D3·D4·D7)은 요지만 두고 exact-match/consent verification 깊이는 형제 [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] 로 **위임**(재진술 안 함, Single-Owner).
### 1. Keycloak Admin Console — Google IdP 등록 입력 매핑 (D1·D2·D5·D6)
> **Trace**: D1(`KC-GIDP-C1` nav) · D2(`KC-GIDP-C2` credential 입력) · D5(`GOIDC-C5` discovery) · D6(`KC-TRUSTEMAIL-C1` Trust Email).
>
> - **UNSUPPORTED_IMPL_DECISION**: Keycloak 25.x/26.x admin UI 의 정확한 필드 라벨·위치(`Use discovery endpoint` 토글 명칭, `Trust Email` 토글 위치, discovery 입력 시 endpoint 필드 read-only 여부)는 코드/UI 미확인 → `planned`. trade-off: 학습 단계엔 `planned`, Admin UI 캡처(Claims To Verify) 시 확정.
| 단계 / 필드 | 값 / 명세 | Trace |
|---|---|---|
| IdP 추가 | Identity Providers → Add provider → **Google** (alias=`google`) | D1, `KC-GIDP-C1` |
| Client ID / Client Secret | Google Console 발급값 입력 | D2, `KC-GIDP-C2` |
| Use discovery endpoint | ON → `https://accounts.google.com/.well-known/openid-configuration` 입력 → authorize/token/userinfo/jwks endpoint 자동 발견 | D5, `GOIDC-C5` |
| Default Scopes | `openid profile email` (기본값 유지) | D4, `KC-GIDP-C5` |
| Trust Email | **OFF (`false`)** 명시 설정 (§2) | D6, `KC-TRUSTEMAIL-C1` |
| Display Name on Login Page | 로그인 버튼 라벨 (예: "Sign in with Google") — `UNSUPPORTED_IMPL_DECISION` (cosmetic 표시값, 동작·보안 무관 → 임의) | TODO 참조 |
### 2. `trustEmail` 필드 — ON/OFF 의미 + 값 선택 (D6)
> **Trace**: D6 + `KC-TRUSTEMAIL-C1`/`C2`/`C3`.
>
> - **UNSUPPORTED_IMPL_DECISION**: default 값이 이미 OFF 인지 **공식 문서 미명시**(fetch 한 `configuration.adoc` 에 default 문장 부재, self-grep 확인) → Admin UI 캡처로만 확정. trade-off: default 불확실성을 피하려면 **OFF 를 명시적으로 설정**(권장) — default 에 의존하지 않음.
| 상태 | 동작 | 근거 |
|---|---|---|
| ON (`true`) | IdP 제공 email 을 신뢰 → realm email 검증 skip; IdP 가 email 검증 여부를 advertise(예: `email_verified`)하면 그 값으로 (un)marking; Sync Mode `FORCE` 면 매 로그인 재평가 | `KC-TRUSTEMAIL-C1`/`C2`/`C3` |
| OFF (`false`, **채택**) | realm email 검증 절차 유지 — Google email 을 무조건 verified 로 신뢰하지 않음 (ON 의 반대 함의) | `KC-TRUSTEMAIL-C1` |
| default | **미확정** — 공식 문서 미명시 → OFF 를 명시 설정 권장 | `UNSUPPORTED_IMPL_DECISION` |
### 3. Google Cloud Console — client 등록 요지 (D2·D3·D4·D7) + 형제 위임
> **Trace**: D2(`KC-GIDP-C2`) · D3(`KC-GIDP-C3`/`C4`) · D4(`KC-GIDP-C5`) · D7(`GOOGLE-OAUTHPOLICY-C1`/`C3`).
>
> - **UNSUPPORTED_IMPL_DECISION**: JavaScript origins 비움 여부·redirect URI byte-level(trailing slash/case)·consent-screen verification 심사·URL 변경 갱신 절차는 형제 [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] §1 이 owner — **여기서 재진술하지 않음**(Single-Owner, §Audit `SINGLE_OWNER_TENSION`).
| 필드 | 값 | Trace | Note |
|---|---|---|---|
| Application type | **Web application** | D2 | server-to-server confidential client |
| Authorized redirect URIs | `https://<kc-host>/realms/<realm>/broker/google/endpoint` (Keycloak 표시값 그대로 복사) | D3, `KC-GIDP-C3`/`C4` | exact-match·다환경 URI·JS origins → 형제 redirect-uri-policy §1 위임 |
| Scopes (consent) | `openid email profile` | D4, `KC-GIDP-C5` | verification 심사 상세 → 형제 redirect-uri-policy D5 |
| 환경 분리 단위 | 현재 단일 client / 실배포 tier 발생 시 별도 project | D7, `GOOGLE-OAUTHPOLICY-C1` | project 분리는 조건부(Google "production" 정의 충족 시). credential never-commit(`C3`)은 **항상** |
## 엣지·실패·의존
> R4 캡처용. 정상 등록 경로 외에 *구현 중 부딪힐* 실패/엣지 + 본 branch 가 consume/제공하는 다른 계약. 실 적용 전이므로 "예상" 경로.
- **실패·엣지 경로**:
- **redirect_uri mismatch (D3)**: Google 등록값 ≠ Keycloak broker endpoint(trailing slash / scheme / path 오타) → Google `redirect_uri_mismatch` 로 인증 차단. 기대: Keycloak 표시값 그대로 복사. byte-level 상세·갱신 절차 → 형제 redirect-uri-policy.
- **discovery 실패 / endpoint 변경 (D5)**: Google `.well-known` 미응답 또는 endpoint URL 변경 시 broker token 교환 실패. Keycloak 의 discovery 캐시/재fetch 주기는 미확인(`needs-confirmation`).
- **`trustEmail=true` 오설정 (D6)**: IdP email을 무조건 신뢰하면 realm 자체 검증이 건너뛰어질 수 있다. core 방어는 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4의 silent auto-link 차단이며 본 D6는 defense-in-depth다. First Broker Login의 버전별 보안 caveat는 공식 advisory가 raw로 보존되기 전까지 `needs-confirmation`으로만 취급한다.
- **Trust Email + Sync Mode `FORCE` 상호작용 (D6)**: `FORCE` 면 매 로그인마다 email verified 재평가(`KC-TRUSTEMAIL-C3`) → Sync Mode 값 owner 형제 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3 에 의존. `IMPORT`/`LEGACY` 재평가 여부는 이 자료 미명시.
- **`client_secret` 노출 (D7)**: Keycloak DB plaintext + docker-compose env → git 커밋 누출 위험. 기대: never-commit(`GOOGLE-OAUTHPOLICY-C3`) + vault/secret manager. **단일 client 공유 시 유출 blast radius 가 전 환경**.
- **`trustEmail` default 불확실 (D6)**: 명시 설정 없이 default 에 의존하면 버전별 default 상이 위험 → **OFF 를 명시 설정**해 회피.
- **다른 계약 의존**:
- (본 branch 가 **제공** → 역방향 consume) [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] `D4` 가 본 `D6`(trustEmail=false)를 defense-in-depth 로 consume. 본 D6 값이 바뀌면 그 방어 전제 변함.
- (제공) [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] Scenario C 가 본 `D6``email_verified=false` takeover 방어 입력으로 consume.
- (consume) [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] `D1`~`D4` — redirect URI exact-match·다환경 URI 의 deep owner. 본 `D3` 는 그 등록 결과(Keycloak Redirect URI → Google)를 사용.
- (consume) [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] `D3`(Sync Mode) — D6 의 `FORCE` 재평가 상호작용이 그 값에 종속.
- (consume) [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]] (부모) — broker endpoint host(`<kc-host>`) 를 결정. host 가 바뀌면 D3 redirect URI 도 재등록 필요.
## 검증해야 할 주장
> 본 sub-sub 가 `documented-only` 라도, 만약 P3B 구현 시점에 도달하면 검증해야 할 주장.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| Keycloak 25.x admin UI 의 정확한 Google IdP 등록 navigation 경로 | cited `keycloak-google-idp-setup` 은 legacy gitbook 미러; 신규 UI 와 차이 가능 | Keycloak 25.x docker 컨테이너 실행 후 admin UI 캡처 | `needs-confirmation` |
| `https://<kc-host>/realms/<realm>/broker/google/endpoint` 가 Keycloak 의 정확한 callback URL 형식 | path 형식의 vendor verbatim 부재 | Keycloak admin UI 의 "Redirect URI" 자동 표시값 캡처 + Server Admin Guide raw 발췌 보강 | `needs-confirmation` |
| `trustEmail = false` 가 Keycloak Google IdP 의 default 값 | cited raw 에 verbatim 부재 | Keycloak 25.x docker 실행 + admin UI 에서 default toggle 상태 캡처 | `needs-confirmation` |
| discovery URL 입력 시 Keycloak admin UI 가 endpoint 4개 (authorize/token/userinfo/jwks) 를 자동 채움 | UI 동작의 verbatim 인용 없음 | discovery URL 입력 후 endpoint 필드가 read-only / 자동 채워지는지 admin UI 캡처 | `needs-confirmation` |
| Google `email_verified` claim 이 항상 true 인 사용자만 신뢰 가능 (자동 link 허용) | Google `email_verified` 의 보장 수준 verbatim 인용이 `google-openid-connect-oidc` 에 부재 (C4 는 `email` claim 만 다룸) | Google OIDC claims table 추가 발췌 + `email_verified=false` 시나리오 (예: Gmail unverified alias) 테스트 | `needs-confirmation` |
| 환경별 project 분리 의무(`GOOGLE-OAUTHPOLICY-C1`)가 이 학습 프로젝트에 실제로 집행되는가 — Google 이 "production" 정의 미충족(personal use) 앱에도 verification-review 에서 project 미분리를 지적하는지 | 공식 문서는 "production" app 에만 의무로 명시(`GOOGLE-OAUTHPOLICY-C2` personal-use 예외) — 실제 집행 관행은 문서 범위 밖 | Google Trust & Safety verification 절차 raw 추가 또는 실제 Testing→Published 전환 시 관찰 | `needs-confirmation` |
| GCP OAuth consent screen 이 project 레벨 리소스여서 별도 client(같은 project) 로는 env 별 branding 분리가 안 되는가 (D7 Alt1 vs Alt3 차별점) | D7 조사에서 구조적 추론으로만 제시(verbatim 부재) | GCP Console 에서 같은 project 의 2 client 가 consent screen 을 공유하는지 실제 확인 | `needs-confirmation` |
## Audit & Findings
> 2026-07-16 `/branch-spec` 자동조사(공식 문서 web 조사 + 형제 corpus 정독) 결과. 사용자 작성 결정/메모는 verbatim 보존, 아래는 정합 권고·정정·근거 승급만 (CLAUDE.md §11).
- **D6_UPGRADE (UNSUPPORTED → official-vendor-doc, 값 선택분만)**: D6 `trustEmail=false`*값 선택* 근거를 [[raw/official-docs/keycloak-identity-provider-trust-email-official]] (`KC-TRUSTEMAIL-C1~C3`: ON=realm 검증 skip / `email_verified` (un)marking / `FORCE` 재평가)로 승급. **단 "false 가 default" 서브클레임은 여전히 `needs-confirmation`** — fetch 한 `configuration.adoc` 에 default 문장 부재(self-grep `default` = Trust Email 무관 1건뿐). 목표/WHY 의 "기본값이자 권장값" 중 *권장값* 만 grounded, *기본값* 은 미확정(§목표 정정 callout). 조건부 신뢰(email_verified 일 때만 skip)는 Keycloak 공식 미지원(GitHub #8622 미병합) — custom SPI 필요, 학습 범위 밖.
- **D7_RESCOPE (UNSUPPORTED → official-vendor-doc 조건부)**: Google 공식(`GOOGLE-OAUTHPOLICY-C1`)이 배포 tier 별 **project** 분리를 명시하나 "production" app(`GOOGLE-OAUTHPOLICY-C2`) 스코프 — 본 개인 학습 프로젝트는 personal-use 예외라 **현재 의무 아님**(선제적 설계 근거로만 인용). credential never-commit(`GOOGLE-OAUTHPOLICY-C3`)은 production 스코프 밖이라 **무조건** 적용. 원 "환경별 OAuth **client** 분리"는 "**project** 분리(client 자동 분리 결과)"로 재프레이밍 — client(같은 project) 분리만으로는 consent-screen branding 분리 불가(구조적 추론 → Claims To Verify).
- **UNARCHIVED_SECURITY_CANDIDATE (OUT_OF_BRANCH_SCOPE)**: 이전 조사에서 First Broker Login의 cross-session verification 취약점 후보가 기록됐지만 공식 advisory가 raw로 보존되지 않았다. 실재·영향 버전·patch 버전은 현재 `needs-confirmation`이며 FACT나 배포 하한으로 사용하지 않는다. 보존 후 owner [[raw/branch-notes/feature-keycloak-first-broker-login-flow]]에서 version gate를 결정한다.
- **SINGLE_OWNER_TENSION (D3 vs 형제 redirect-uri-policy, Should-fix)**: D3(redirect URI 등록)은 형제 [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] D1~D4(GOOGLE-REDIR 근거 5개 — exact-match·다환경·JS origins·consent verification 의 deep owner)와 실질 중복. 본 branch 는 "Keycloak Redirect URI → Google 복사" **등록 step** 만 유지하고 exact-match/byte-level/갱신 depth 는 그 형제로 위임(§구현 가이드 §3 reference-only). 사용자 결정 영역이라 자동 rewrite 안 함 — `/sync` 대조 권고.
- **STALE_REVERSE_REF (Should-fix, `/sync` 대상)**: D6 가 `UNSUPPORTED_DECISION``official-vendor-doc`(값 선택분) 로 승급됐으므로, 본 D6 를 "자체 `UNSUPPORTED`"로 요약·의존하는 역참조들이 부분 stale: [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] (L27·L65·L80·L135·L169·L196 + DEPTH_LOOP_1 F3), [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] (L206·L214). **의미는 안전한 방향으로만 바뀜**(UNSUPPORTED → grounded; 두 형제의 "core 방어는 trustEmail 과 무관" 논리는 그대로 유효) → 비차단. consistency-contract §전파 precedent(account-linking 자신의 D3 승급을 `/sync` 로 남긴 것)와 동일하게 **이번 fill 에선 형제 재작성 안 하고 `/sync` 로 위임**. `default=false` 는 여전히 needs-confirmation 이라 형제들의 "trustEmail 값 확정은 그 branch" 서술은 *부분적으로만* 갱신 필요.
- **NO_GROUND_TRUTH (한계 명시)**: 코드 repo `/home/donghyeon/workspace/keycloak-patterns/` 미생성 → `actually-implemented` 주장 불가, 모든 구현 detail `planned`/`needs-confirmation`. §참조의 ca-tmpl ground truth(registries/error-codes 등)는 **본 프로젝트와 무관**(keycloak-patterns = 별도 repo) — 계약값 검증 대상 아님. coverage 게이트도 면제(`governing_docs` 미지정 + related_projects=keycloak-patterns, `rules/coverage-gate.md` §7).
## 마주친 문제
- 아직 없음(문서 단계).
## 묶음
<!-- GENERATED: sources:start -->
- [[raw/official-docs/google-oauth2-policies-environment-separation-official]]
- [[raw/official-docs/google-oidc-discovery-spec]]
- [[raw/official-docs/google-openid-connect-oidc]]
- [[raw/official-docs/keycloak-google-idp-setup]]
- [[raw/official-docs/keycloak-identity-broker-spi]]
- [[raw/official-docs/keycloak-identity-brokering-overview-official]]
- [[raw/official-docs/keycloak-identity-provider-trust-email-official]]
<!-- GENERATED: sources:end -->
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role]]
- [[raw/branch-notes/feature-keycloak-three-leg-trust-chain]]
<!-- GENERATED: branches:end -->
> 본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
### 근거 자료
- [[raw/official-docs/keycloak-google-idp-setup]]
- [[raw/official-docs/google-openid-connect-oidc]]
- [[raw/official-docs/keycloak-identity-brokering-overview-official]]
- [[raw/official-docs/keycloak-identity-provider-trust-email-official]]
- [[raw/official-docs/google-oauth2-policies-environment-separation-official]]
### 오류 기록 (이 sub-sub-branch 작업 중 발생)
- (없음 — 현재 documented-only 단계)
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (없음 — Phase 3 실 구현 단계에 누적)
## 관련 일일 노트
## 완료 후 정리
- PR 링크: (없음, 문서까지만)
- 머지 결과 / 배포 환경: 없음 (`documented-only`)
- **wiki 추출 대상:** 없음. P1B sub-sub-branch는 root 정책상 wiki/projects/ 승급 안 함.
- **추출하지 않을 항목:** 본 sub-sub-branch 전체 (`documented-only` / `planned`).