379 lines
33 KiB
Markdown
379 lines
33 KiB
Markdown
---
|
||
title: branch / feature-keycloak-edge-forwardauth-google-federation (P1B Edge Forward Auth + Google federation)
|
||
source_type: branch-note
|
||
status: raw
|
||
id: BR-KEYCLOAK-CHILD-8F3B8B4E
|
||
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-edge-forwardauth-google-federation
|
||
parent_branch: feature-keycloak-patterns
|
||
related_projects: [keycloak-patterns]
|
||
tags: [branch, keycloak-patterns, auth, oauth2, oidc]
|
||
created: 2026-05-25
|
||
target_merge:
|
||
status_label: in-progress
|
||
contract_packet_sha256: 0d0cfce93fa2e7290518d60b46e5559d479877936fb26cc8dacbf2232cb0d8d3
|
||
---
|
||
|
||
# branch: feature-keycloak-edge-forwardauth-google-federation (P1B Edge Forward Auth + Google federation)
|
||
|
||
> Layer: `raw/branch-notes/` — root [[raw/branch-notes/feature-keycloak-patterns]]의 sub-branch.
|
||
> 패턴 ID: **P1B** — Edge ForwardAuth (oauth2-proxy / Traefik) + Keycloak에 Google을 외부 IdP로 brokering.
|
||
> 비교 대상: [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] (P1A: 같은 배치, Google 없음).
|
||
> `status_label`: `in-progress`
|
||
|
||
<!-- section-id: branch-parent -->
|
||
## 부모 (필수)
|
||
|
||
- **Parent branch (root)**: [[raw/branch-notes/feature-keycloak-patterns]]
|
||
- **Parent project**: [[raw/project-notes/keycloak-patterns-overview]]
|
||
- **Sibling sub-branches**:
|
||
- [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] — P1A (Edge, no Google)
|
||
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] — P2A
|
||
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] — P2B
|
||
- [[raw/branch-notes/feature-keycloak-single-ec2-no-google]] — P3A
|
||
- [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] — P3B
|
||
|
||
<!-- 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 변형이다 | Edge ForwardAuth에 Google federation을 결합한 cross-cutting 변형으로 분류한다 | [[raw/project-notes/keycloak-patterns-overview]] |
|
||
|
||
<!-- section-id: branch-local-decisions -->
|
||
### 브랜치 지역 결정
|
||
|
||
| Decision ID | Decision | Relation | Supporting Claims | Status |
|
||
|---|---|---|---|---|
|
||
| D1 | First Login Review Profile을 기본 off로 둔다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
|
||
| D2 | automatic email linking 대신 manual confirm을 사용한다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
|
||
| D3 | environment별 Google OAuth client를 분리한다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
|
||
| D4 | Google scope를 openid profile email로 제한한다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
|
||
| D5 | persistent federation key policy는 child owner를 소비한다 | `local` | [[raw/project-notes/keycloak-patterns-overview]] | `proposed` |
|
||
|
||
<!-- section-id: declared-overrides -->
|
||
### 선언한 예외
|
||
|
||
| Override ID | Overrides | Reason | Approval | Status |
|
||
|---|---|---|---|---|
|
||
|
||
없음.
|
||
<!-- GENERATED: branch-contract:end -->
|
||
|
||
## 묶음 (자식 sub-sub-branches)
|
||
|
||
<!-- GENERATED: sources:start -->
|
||
- [[raw/company-tech-blogs/keycloak-google-login-codemancers]]
|
||
- [[raw/official-docs/google-openid-connect-oidc]]
|
||
- [[raw/official-docs/keycloak-first-login-flow]]
|
||
- [[raw/official-docs/keycloak-google-idp-setup]]
|
||
<!-- GENERATED: sources:end -->
|
||
|
||
- [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]]
|
||
- [[raw/branch-notes/feature-keycloak-first-broker-login-flow]]
|
||
- [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]]
|
||
- [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]]
|
||
|
||
> Sources는 하단 "외부 근거" 섹션. Errors/Interview/Lectures/Blog 는 현재 없음.
|
||
|
||
<!-- section-id: branch-goal -->
|
||
## 목표
|
||
|
||
P1A에 외부 IdP(Google)가 붙으면 토큰 흐름이 어떻게 확장되는지를 명확히 한다.
|
||
|
||
**핵심 통찰:** **edge proxy 입장에서는 변화가 없다.** oauth2-proxy는 여전히 Keycloak 한 곳에만 redirect 하고, Keycloak이 발급한 Keycloak access token만 받는다. Google federation은 **Keycloak 내부에서 일어나는 외부 IdP brokering 흐름**이며, edge / backend 입장에서는 투명(transparent)하다.
|
||
|
||
면접 / 설계 시 자주 헷갈리는 지점:
|
||
- "Google 로그인을 붙이면 backend가 Google ID token을 검증해야 하나?" → **아니다.** P1B backend trust는 [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] D3 — P1A와 같은 header-only 계약이며 Google token과 Keycloak token을 backend로 전달하지 않는다.
|
||
- "edge proxy가 Google client secret을 알아야 하나?" → **아니다.** Google credential은 Keycloak이 보관·사용.
|
||
- "추가되는 trust hop은 어디인가?" → **Google → Keycloak.** P1A 대비 추가된 신뢰 경계는 이 한 hop.
|
||
|
||
<!-- section-id: branch-scope -->
|
||
## 범위
|
||
|
||
### 포함 범위
|
||
|
||
- P1B 컴포넌트 다이어그램 (P1A 대비 추가 컴포넌트 표시)
|
||
- P1A 대비 추가되는 토큰 교환 단계 (4–8) 명시
|
||
- Google federation 시 추가되는 신뢰 경계와 보안 surface
|
||
- First Login Flow 정책 결정 지점 (Review Profile / Account Linking)
|
||
- P1A 대비 장단점 / 운영 비용 비교
|
||
|
||
### 제외 범위
|
||
|
||
- 실제 구현 (P1B는 문서까지만 — root의 implementation 대상은 P3A)
|
||
- Google 외 IdP (GitHub / Facebook / Apple). Google만.
|
||
- Keycloak Authentication Flow custom code (Java SPI). 설정 옵션 수준까지.
|
||
- prod 환경 Google API rate limit / quota 분석.
|
||
|
||
## 근거 (필수, 최소 1개+)
|
||
|
||
> 본 sub-branch의 P1B (Edge + Google federation) 채택 근거. 상세 대안 비교는 §외부 근거 / 대안 조사 참조.
|
||
|
||
| Source | 정당화하는 결정 |
|
||
|---|---|
|
||
| [[raw/official-docs/keycloak-identity-brokering-overview-official]] | Keycloak Identity Broker 공식 — Google IdP brokering 패턴 채택 근거 |
|
||
| [[raw/official-docs/keycloak-google-idp-setup]] | Keycloak Google IdP setup 절차 — 구성 단계 근거 |
|
||
| [[raw/official-docs/google-openid-connect-oidc]] | Google OIDC 표준 (issuer, scopes, claims) — Google IdP 표준 동작 근거 |
|
||
| [[raw/official-docs/keycloak-first-login-flow]] | First Broker Login Flow — Account Linking 정책 근거 |
|
||
| [[raw/company-tech-blogs/keycloak-google-login-codemancers]] | Keycloak + Google 통합 실무 사례 (참고) |
|
||
|
||
## 컴포넌트 다이어그램
|
||
|
||
```
|
||
Browser
|
||
│
|
||
▼
|
||
Ingress (nginx / Traefik)
|
||
│
|
||
▼
|
||
ForwardAuth (oauth2-proxy) ────► Keycloak (realm: app)
|
||
│ (사용자가 "Sign in with Google" 클릭)
|
||
▼
|
||
Google OIDC
|
||
(authorize / token / userinfo)
|
||
│ (Google ID token + access token)
|
||
▼
|
||
Keycloak
|
||
(First Login Flow:
|
||
Google sub/email → Keycloak user
|
||
매핑 또는 신규 생성)
|
||
│ (Keycloak access token 발급)
|
||
▼
|
||
oauth2-proxy
|
||
│ (proxy 세션 cookie 셋팅 + 헤더 주입)
|
||
▼
|
||
Ingress
|
||
│
|
||
▼
|
||
Backend
|
||
- edge가 주입한 trusted header만 신뢰
|
||
- JWT/Google token은 보지 않음
|
||
```
|
||
|
||
핵심 표시:
|
||
- **edge proxy ↔ Keycloak 구간 = P1A와 동일.**
|
||
- **Keycloak ↔ Google 구간 = P1B에서 새로 추가된 leg.**
|
||
- **backend trust = [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] D3 — edge-injected header only.** Google federation은 이 경계를 바꾸지 않는다.
|
||
|
||
## 토큰 교환 sequence (P1A 대비 추가 단계 포함)
|
||
|
||
P1A 단계와 일치하는 부분은 그대로, Google federation 분기만 새 번호로 표기.
|
||
|
||
1. (P1A 1과 동일) 사용자 브라우저가 보호 리소스 GET → Ingress → oauth2-proxy.
|
||
2. (P1A 2와 동일) oauth2-proxy: 세션 없음 → Keycloak `authorize` redirect.
|
||
3. (P1A 3과 동일) Keycloak 로그인 페이지 표시.
|
||
4. **(추가)** Keycloak 로그인 UI에 "Sign in with Google" 버튼 노출 (Identity Provider로 Google 등록 시 자동).
|
||
5. **(추가)** 사용자 버튼 클릭 → Keycloak → Google `authorize` endpoint redirect (`https://accounts.google.com/o/oauth2/v2/auth`, scope=`openid profile email`).
|
||
6. **(추가)** 사용자 Google 로그인 → Google → Keycloak broker callback (`/realms/<realm>/broker/google/endpoint`, `code` 전달).
|
||
7. **(추가)** Keycloak → Google `/token` (`https://oauth2.googleapis.com/token`), Google ID token + access token 수신.
|
||
8. **(추가)** Keycloak: Google ID token의 `sub`(영구 식별자) / `email` claim → **First Login Flow** 진입.
|
||
- 기존 federated user 있음 → 그대로 매핑된 Keycloak user 사용.
|
||
- 없고 email match로 기존 local user 있음 → Handle Existing Account 서브플로우 (자동 링크 / 수동 confirm).
|
||
- 둘 다 없음 → 신규 Keycloak user 생성 (Review Profile 옵션에 따라 확인 페이지).
|
||
9. (P1A 5와 동일) Keycloak → oauth2-proxy callback (`/oauth2/callback`, `code` 전달). oauth2-proxy → Keycloak `/token`. **Keycloak access token + refresh token + ID token 발급.**
|
||
10. (P1A 6–7과 동일) oauth2-proxy: 세션 cookie 셋팅 + 헤더(`X-Auth-Request-Email` 등) 주입 후 backend로 forward. P1B 기본 계약은 bearer token을 전달하지 않고 backend가 edge header만 신뢰한다.
|
||
|
||
## 신뢰 경계 (P1A 대비 변화)
|
||
|
||
| 경계 | P1A | P1B |
|
||
|------|-----|-----|
|
||
| Browser ↔ oauth2-proxy | TLS, 세션 cookie | 동일 |
|
||
| oauth2-proxy ↔ Keycloak | TLS, client secret | 동일 |
|
||
| Keycloak ↔ Google | — | **신규.** TLS, Google OAuth client secret (Keycloak이 보관) |
|
||
| oauth2-proxy ↔ Backend | trusted identity header (bearer token 미전달) | 동일 |
|
||
| Backend의 token 검증 | 없음 — edge에서 인증 종결, header-only | 동일 — Google federation만 추가 |
|
||
|
||
신규 trust hop = **Google → Keycloak 한 개.** Google ID token signature는 Keycloak이 검증하고 oauth2-proxy는 Keycloak 세션을 만든다. 그 이후 backend trust는 P1A와 같은 header-only 경계다.
|
||
|
||
## 장점 / 단점 vs P1A
|
||
|
||
### 장점
|
||
|
||
- 사용자가 **Google 계정으로 로그인 가능** → 별도 비밀번호 관리 불필요. UX 개선.
|
||
- 조직이 Google Workspace 사용 중이면 사실상의 SSO 통합 (사내 Google 계정 그대로 사용).
|
||
- Keycloak이 brokering 하므로 **edge/backend 코드 변화 0** — P1A에서 Identity Provider만 추가 설정.
|
||
- 다른 외부 IdP(Microsoft / GitHub) 추가 시에도 동일 패턴으로 확장 가능 (broker만 추가 등록).
|
||
|
||
### 단점
|
||
|
||
- **외부 의존:** Google OIDC downtime / rate limit 시 신규 로그인 불가 (이미 발급된 Keycloak 세션은 영향 없음).
|
||
- **사용자 매핑 정책 운영 부담:** First Login Flow / Account Linking 정책 결정 필요. 잘못 설정 시 보안 이슈 (자동 email match linking → account takeover 위험).
|
||
- **보안 surface 확장:**
|
||
- Google OAuth client secret이 Keycloak DB(또는 vault)에 저장됨.
|
||
- Google Cloud Console의 redirect URI 등록 관리 (환경별 OAuth client 분리 필요).
|
||
- Google 측 권한 변경(예: scope 변경, OAuth verification 요구) 시 영향 받음.
|
||
- **개인정보 / 동의 흐름 추가:** Google scope 동의 화면, GDPR 등 데이터 처리 정책 영향.
|
||
- **디버깅 복잡도:** 로그인 실패 시 oauth2-proxy / Keycloak / Google 3-leg 중 어디서 실패했는지 추적 필요 (로그 corr id 설계 중요).
|
||
|
||
## 결정 사항 (decisions)
|
||
|
||
본 sub-branch는 문서까지만 (`documented-only`)이므로 실제 환경 결정은 없음. **만약 구현한다면** 권장 기본값:
|
||
|
||
- **D1 — First Login Flow / Review Profile:** OFF (Google이 email/profile 제공하므로 불필요). 단, 신규 사용자 동의 페이지가 필요한 비즈니스 요건이면 ON.
|
||
- **D2 — Account Linking:** **수동 confirm.** 공식 문서가 "automatic linking by email = potential security hole" 명시. email match 시 사용자가 명시적으로 link 확인하도록.
|
||
- **D3 — Google OAuth client 분리:** dev / staging / prod 환경별 별도 OAuth client. redirect URI 충돌 방지.
|
||
- **D4 — scope:** `openid profile email`만. (추정 — 추가 scope 요청 시 Google OAuth verification 이 트리거될 수 있으나 인용 raw 가 enumerate 안 함, 별도 raw 확보 전까지 근거 미보증.)
|
||
- **D5 — persistent federation key requirement:** [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1 — concrete key policy owner. 본 parent는 안정적인 외부 subject를 사용한다는 P1B invariant만 consume한다.
|
||
|
||
`needs-confirmation`:
|
||
- Keycloak 세션 만료 시 Google refresh token으로 자동 갱신 가능 여부 (Keycloak이 Google refresh token을 보관하나? 정책상 사용자 재로그인이 일반적).
|
||
- Google account 삭제 / suspend 시 Keycloak local user 자동 비활성화 여부.
|
||
|
||
## 결정-근거 매핑
|
||
|
||
> P1B 권장 기본값 결정과 raw source claim 매핑. company-tech-blog (`codemancers`) claim 은 보조 근거 — 공식 best practice 로 격상 금지 (CLAUDE.md §5).
|
||
|
||
> `선택 조건` 열(R2): 각 결정이 "어떤 조건일 때 이 값, 다른 조건이면 어떤 대안" 인지. 상세 근거는 §결정 사항 prose.
|
||
|
||
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|
||
|---|---|---|---|---|---|
|
||
| D1 | First Login Flow / Review Profile = OFF (Google이 email/profile 제공하므로 불필요) | Google 이 `email`+`profile` 을 제공 → `Off`. 신규 사용자 동의/추가 attribute 수집이 비즈니스 요건이면 `On`, mandatory attr 부재 대비 fallback 만이면 `missing` (KC-FLF-C4 의 3-mode) | `raw/official-docs/keycloak-first-login-flow.md#KC-FLF-C4`, `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C5`, `raw/official-docs/google-openid-connect-oidc.md#GOIDC-C4` | `official-vendor-doc (Keycloak) + official-standard (Google OIDC)` | KC-FLF-C4 의 "mandatory information" 정확한 목록 (locale 포함 여부 등) 미확정 — `Off` mode 에서도 누락 시 자동 fallback 동작 확인 필요 |
|
||
| D2 | Account Linking = 수동 confirm (자동 email link 금지) | 외부 IdP email 을 항상 신뢰할 수 없음이 기본(KC-FLF-C2 공식 경고) → 항상 Confirm Link. 자동 email link 는 통제된 신뢰 환경에서도 공식 경고 대상이라 채택 안 함 (대안 없음) | `raw/official-docs/keycloak-first-login-flow.md#KC-FLF-C2`, `raw/official-docs/keycloak-first-login-flow.md#KC-FLF-C3` | `official-vendor-doc` (Keycloak 공식 security warning 명시) | "Confirm Link Existing Account" 가 Keycloak version 별 default flow 에 포함되는지 vs 별도 추가인지 — KC-FLF-C3 의 "Does not prove" 에 명시 — version 별 확인 필요 |
|
||
| D3 | dev / staging / prod 환경별 별도 Google OAuth client | 환경별 redirect URI/도메인이 다름 → 환경당 별도 OAuth client. 단일 도메인·단일 환경이면 client 1개 + 다중 redirect URI 로도 가능 | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C3`, `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C4`, `raw/company-tech-blogs/keycloak-google-login-codemancers.md#CM-KC-GG-C4` (보조 — company case study, 공식 best practice 아님) | `official-vendor-doc (Keycloak) + company-case-study (codemancers, 보조)` | "환경별 분리가 redirect URI 충돌 방지에 필수" 는 일반 운영 원칙으로 raw claim 들이 직접 명시하지 않음 — KC-GIDP-C4 의 wildcard / 부분 매칭 허용 여부가 raw 범위 밖 |
|
||
| D4 | scope = `openid profile email` 만 | `email`/`profile` 매핑만 필요 → Keycloak default 3 scope 유지. `hd`(도메인 제한)·groups 등 추가 사용자 데이터가 필요할 때만 scope 추가 | `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C5`, `raw/official-docs/google-openid-connect-oidc.md#GOIDC-C4` | `official-vendor-doc (Keycloak default) + official-standard (Google OIDC scope contract)` | "추가 scope 요청 시 Google OAuth verification 트리거" 는 본 raw 들이 직접 enumerate 하지 않음 — Google OAuth verification 정책 별도 raw 보존 필요 |
|
||
| D5 | P1B는 stable external subject를 요구하며 concrete persistent federation key policy를 직접 소유하지 않음 | key 선택·변경은 child owner에서만 수행. parent는 그 결과를 P1B topology invariant로 consume | [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1 — persistent Google federation key owner | `delegated` | Keycloak 저장 메커니즘과 initial collision locator의 차이는 child에서 추적; parent에 복제하지 않음 |
|
||
|
||
## 구현 가이드
|
||
|
||
> **본 브랜치는 `documented-only` 설계 hub** — P1B topology와 pattern-level 요구만 소유하고 Google brokering의 mutable 설정은 자식 브랜치(§Cluster 4개)에 위임한다. 아래 parent D-row는 자식 owner의 값을 복제하지 않고 pointer로 consume한다. **모든 항목 등급 `planned`** — 구현 repo(`keycloak-patterns/`)가 아직 없어 코드로 확정된 것은 0개(`NO_GROUND_TRUTH`, ground truth = 공식 raw 문서). 코드 확인 후 등급 승격.
|
||
>
|
||
> **3-rule 준수**: 각 sub-section 은 Decision ID + Supporting Claim ID 를 Trace. 근거 raw 가 *원칙* 만 주고 *detail* 은 안 주는 cell 은 `UNSUPPORTED_IMPL_DECISION` + trade-off 한 줄. 본 브랜치 결정 범위 밖 detail(Java SPI custom code 등)은 §범위 out-of-scope 로 이미 배제.
|
||
|
||
### 1. Google IdP 등록 (realm config) — D3 · D4 + delegated D5
|
||
|
||
> **Trace**: D3(환경별 client)·D4(scope). [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1 — persistent federation key owner. IdP client 설정 owner는 [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]].
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: (a) redirect URI 정확한 path 형식 — raw 가 명시 안 함(KC-GIDP-C3 "does not prove"), Admin Console 자동표시값을 복사(Claim To Verify #6 가 추적). trade-off: path 를 하드코딩하면 `KC_HTTP_RELATIVE_PATH`/host 조합 변화에 취약 → UI 표시값 복사가 안전. (b) discovery endpoint cache/retry 동작 — GOIDC-C5 는 URL 만 줌(Claim To Verify #1). (c) 환경별 client 분리 — official 미보증 운영 추론(KC-GIDP-C3/C4 는 양방향 등록까지만). trade-off: 단일 client 다중 redirect URI 도 가능하나 환경 간 실수 유출 위험 → 환경별 분리가 안전측.
|
||
|
||
| 설정 항목 | 값 / 경로 | Trace | 등급 |
|
||
|---|---|---|---|
|
||
| IdP 등록 진입 | Admin Console → `Identity Providers` → `Add provider` → `Google` | KC-GIDP-C1 | `planned` |
|
||
| Client 자격 | Google 발급 `Client ID` + `Client Secret` 을 Keycloak Google IdP 에 입력 (Keycloak DB/vault 보관) | KC-GIDP-C2, D3 | `planned` |
|
||
| 환경 분리 | dev/staging/prod 각 환경별 Google OAuth client 별도 발급 (redirect URI 충돌 방지). **UNSUPPORTED_IMPL_DECISION** — official 미보증 운영 추론 | D3(운영 추론, KC-GIDP-C3/C4 는 양방향 등록까지만 L1 증명) · CM-KC-GG-C4(보조) | `planned` |
|
||
| Redirect URI | Keycloak `Add Identity Provider` 페이지 표시값 → Google Cloud Console `Authorized redirect URIs` 에 복사. 예상 형식 `https://<keycloak-host>/realms/<realm>/broker/google/endpoint` (**UNSUPPORTED_IMPL_DECISION** — 형식은 raw 인용 밖, UI 표시값 신뢰) | KC-GIDP-C3, KC-GIDP-C4 | `planned` |
|
||
| Scope | Default Scopes = `openid profile email` 유지, 추가 scope 금지(D4 조건) | KC-GIDP-C5, D4 | `planned` |
|
||
| Discovery | "Import from URL" = `https://accounts.google.com/.well-known/openid-configuration` | GOIDC-C5 | `planned` (Claim To Verify #1) |
|
||
|
||
### 2. First Broker Login Flow hardening — D1 · D2
|
||
|
||
> **Trace**: D1(Review Profile)·D2(Confirm Link) / Claims `KC-FLF-C2`, `KC-FLF-C3`, `KC-FLF-C4`. Owner 자식: [[raw/branch-notes/feature-keycloak-first-broker-login-flow]].
|
||
>
|
||
> - **OUT_OF_BRANCH_SCOPE**: flow 구성의 정본은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1 — silent auto-link 방지, [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D2 — verification profile, [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 — custom hard-reject SPI 없이 성립하는 core policy. SPI artifact가 없는 현재 scope에서 `email_verified=false` 전체 hard-reject를 parent acceptance로 주장하지 않는다.
|
||
|
||
| Flow 설정 | 값 | Trace | 등급 |
|
||
|---|---|---|---|
|
||
| Flow 편집 전 복제 | built-in "first broker login" flow 를 복제 후 수정 (원본 훼손 시 외부 IdP 전체 차단 위험) | KC-FLF 운영맥락(L90) | `planned` |
|
||
| Review Profile authenticator | `Off` (Google 이 email+profile 제공). 동의 페이지 필요 시만 `On`, mandatory 부재 대비만이면 `missing` | KC-FLF-C4, D1 | `planned` |
|
||
| Confirm Link Existing Account | required — 자동 email link 금지, 사용자 명시 confirm 강제 (info page: 다른 email 사용 vs link 확인) | KC-FLF-C2, KC-FLF-C3, D2 | `planned` |
|
||
| linking trust policy | [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 — silent auto-link 방지 core를 consume. `email_verified=false` 전체 hard-reject는 custom SPI가 별도 채택될 때만 추가 | delegated | `documented-only`; SPI variant는 out-of-scope |
|
||
|
||
### 3. Account matching identifier (IdP mapper) — D5
|
||
|
||
> **Trace**: D5. [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1 — persistent federation key owner. [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4 — claim→attribute mapper owner.
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: `sub` 를 federated identity primary key 로 매핑하는 정확한 Keycloak IdP mapper 타입/옵션 — 참조 raw 미포함(D5 Open Risk 가 `keycloak-identity-provider-mappers.md` 필요 명시). trade-off: mapper 타입 임의 선택 시 재로그인 매칭 실패 가능 → mapper raw 확보 후 자식 브랜치에서 확정.
|
||
|
||
- Persistent federation key와 mapper 타입/옵션은 위 두 owner D-row를 참조한다. 본 parent는 stable external subject requirement만 유지한다.
|
||
|
||
## 엣지·실패·의존
|
||
|
||
> R4 캡처용. 본 브랜치는 `documented-only` 지만, Google leg 추가로 P1A 대비 새 실패 경로가 생기고, 자식·형제 브랜치와 계약 의존이 있다.
|
||
|
||
- **실패·엣지 경로**:
|
||
- **Google OIDC downtime / rate limit**: 신규 로그인 불가. 이미 발급된 Keycloak 세션은 영향 없음 (Google leg 는 최초 인증 시에만) — §장점/단점 근거.
|
||
- **email 기반 auto-linking 계정 탈취**: opt-in AutoLink가 email 값을 무확인 연결하면 기존 계정 탈취 가능. 방어 정본은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1과 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4이며, 이 parent는 `email_verified=true` hard-reject를 별도 acceptance로 두지 않는다.
|
||
- **redirect URI mismatch**: Google Cloud Console `Authorized redirect URIs` ↔ Keycloak `/broker/google/endpoint` 불일치 시 Google 측 오류. 환경별 client 분리(D3)로 완화 (Claim To Verify #6).
|
||
- **bearer token 경계 누출**: P1B baseline backend에는 access token 자체가 전달되지 않는다. `Authorization` header가 관측되거나 Google token이 backend까지 새면 [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] D3의 header-only trust 경계가 붕괴한 것이다(Claim To Verify #5).
|
||
- **3-leg 디버깅 복잡도**: 로그인 실패 시 oauth2-proxy / Keycloak / Google 중 실패 지점 추적 필요 → corr-id 설계 (§장점/단점).
|
||
- **세션·계정 수명 불일치** (`needs-confirmation`): Keycloak 세션 만료 시 Google refresh 자동 갱신 여부(Claim To Verify #2), Google 계정 삭제/suspend 시 Keycloak local user 미비활성(Claim To Verify #3).
|
||
- **다른 계약 의존**:
|
||
- **Root**: [[raw/branch-notes/feature-keycloak-patterns]] 의 고정 결정 F3(단일 공유 realm + 패턴당 client)·F4(confidential secret = env var, 미커밋)에 의존 — Google client secret 저장 정책은 F4 를 따름.
|
||
- **자식(위임 — detail owner)**: [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]] D3 — client 등록. [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1 — silent auto-link 방지, [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D2 — verification profile, [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 — core linking policy. [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4 — claim mapping. [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] D1 — persistent federation key. Parent는 topology와 요구만 소유한다.
|
||
- **형제(브로커 로직 재사용)**: [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]](P2B), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]](P3B) — 동일 Google brokering 흐름. P3B 실 구현 시 `KC_HOSTNAME` 공개 host 강제(Google redirect_uri 검증)라는 배포측 추가 의존.
|
||
|
||
## 검증해야 할 주장
|
||
|
||
> 본 sub-branch 는 `documented-only`. 만약 구현한다면 검증해야 할 주장 enumerate.
|
||
|
||
| Claim | Why uncertain | How to verify | Status |
|
||
|---|---|---|---|
|
||
| Keycloak 의 Google IdP "Use discovery endpoint" 옵션이 `https://accounts.google.com/.well-known/openid-configuration` 으로 정상 OIDC discovery 수행 | `GOIDC-C5` 가 URL 명시했으나, Keycloak 의 discovery 호출 실제 동작 (cache TTL, retry 정책 등) 별도 검증 | Keycloak Admin Console > Identity Provider > Google > "Import from URL" 클릭 후 endpoint 자동 채워지는지 확인 + keycloak debug log 에서 GET 요청 확인 | `planned` |
|
||
| Keycloak 세션 만료 시 Google refresh token 으로 자동 갱신 가능 여부 | 본 sub-branch §결정 사항 `needs-confirmation` 명시 — Keycloak 이 Google refresh token 을 보관하는지 정책 불명 | Keycloak Admin Console > Identity Provider > Google > "Store Tokens" 옵션 활성화 후 세션 만료 후 동작 관찰 + Keycloak DB `federated_identity` 테이블 검사 | `needs-confirmation` |
|
||
| Google account 삭제 / suspend 시 Keycloak local user 자동 비활성화 여부 | 본 sub-branch §결정 사항 `needs-confirmation` 명시 — Keycloak 에 webhook / polling 메커니즘 없음 (일반적) | Google Cloud Console 에서 test account suspend 후 Keycloak login 시도 → 거부 여부 관찰 (예상: Keycloak 은 모름, login 시점에 Google 측 401 로만 차단) | `needs-confirmation` |
|
||
| First Broker Login Flow 의 "Confirm Link Existing Account" authenticator 가 Keycloak 26.x default flow 에 포함됨 | `KC-FLF-C3` 가 authenticator 존재 명시했으나 version 별 default flow 포함 여부 별도 확인 필요 | Keycloak Admin Console > Authentication > Flows > "first broker login" flow 확인 + "Confirm Link Existing Account" step 존재 여부 | `planned` |
|
||
| P1B가 P1A의 header-only backend trust를 유지하고 Google/Keycloak bearer token을 backend로 전달하지 않음 | [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] D3을 consume하지만 실제 proxy config는 아직 없음 | Google 로그인 후 oauth2-proxy/ingress config와 backend request capture에서 trusted headers 존재, `Authorization` 부재, direct spoof 요청 차단을 확인 | `planned` |
|
||
| Google OAuth client 의 redirect URI 가 `/realms/<realm>/broker/google/endpoint` 형식과 정확히 일치 | `KC-IDP-BROKER-C2` 가 verbatim 부재 명시 — Admin UI 표시값을 신뢰 | Keycloak Admin Console > Identity Provider > Google 페이지의 "Redirect URI" 필드 값을 복사 → Google Cloud Console 의 Authorized redirect URIs 와 byte-level 일치 확인 | `planned` |
|
||
| (Deferred SPI variant) `email_verified=false`를 flow 진입 즉시 hard-reject하는 custom authenticator를 별도 구현할 필요가 있는가 | 현재 corpus에 provider JAR/SPI artifact가 없고 core silent-link 방지는 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4로 성립 | 별도 제품 요구가 생길 때 SPI branch를 만들고 provider JAR, flow export, false-email negative E2E를 함께 검증 | `planned` (현재 acceptance 아님) |
|
||
|
||
|
||
|
||
## 외부 근거 / 대안 조사 (2026-05-25 — P1B Edge + Google IdP Brokering)
|
||
|
||
본 sub-branch의 **Edge ForwardAuth + Google IdP Brokering** 채택에 대한 외부 source. P1A에 외부 IdP federation을 추가하는 방식의 대안 비교.
|
||
|
||
- **채택 결정 (Keycloak IdP Brokering — Google을 외부 IdP로 등록)**:
|
||
- [[raw/official-docs/keycloak-identity-brokering-overview-official]] — Keycloak Identity Broker 공식 (외부 IdP 등록 + first broker login flow)
|
||
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Google IdP setup 절차
|
||
- [[raw/official-docs/google-openid-connect-oidc]] — Google OIDC 표준 (issuer, scopes, claims)
|
||
- [[raw/official-docs/keycloak-first-login-flow]] — First Broker Login Flow (Account Linking 정책)
|
||
- [[raw/company-tech-blogs/keycloak-google-login-codemancers]] — Keycloak + Google 통합 실무 사례
|
||
- **검토한 대안**:
|
||
- **대안 1: SAML federation (Keycloak ↔ Google Workspace SAML)** — 엔터프라이즈 단일 사인온 표준. 그러나 Google OIDC가 더 단순.
|
||
- **대안 2: Google OIDC 직접 (Keycloak 우회)** — 백엔드가 Google ID token 직접 검증. 장: Keycloak 운영 부담 0 / 단: 다중 IdP 통합 어려움, role mapping 직접 작성.
|
||
- **대안 3: Auth0 / Okta (managed multi-IdP SaaS)** — 운영 완전 위임. 단: vendor lock-in, 비용.
|
||
- **대안 4: oauth2-proxy `--provider=google` 직접** — Keycloak 없이 oauth2-proxy가 Google과 직접 통신. 장: Keycloak 제거 / 단: realm/role 관리 불가, 멀티 IdP 통합 불가.
|
||
- **대안 5: AWS Cognito + Google federation** — AWS 종속, 동일 패턴.
|
||
- **비교 핵심**: IdP Brokering의 **본질적 가치는 "코드 변경 없이 IdP 추가"**. SPA/백엔드는 Keycloak만 알면 되고, Google/SAML/LDAP 추가는 Keycloak admin 설정만. AutoLink는 별도 opt-in 위험 기능이며, 본 패턴은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1/D4의 silent-link 방지 core를 따른다.
|
||
|
||
## TODO
|
||
|
||
각 항목 옆에 증거 등급.
|
||
|
||
- [ ] P1B 다이어그램을 Mermaid sequence로 재작성 — 등급: `planned`
|
||
- [ ] P1A vs P1B diff matrix (sequence 단계 / trust boundary / 운영 비용) — 등급: `planned`
|
||
- [ ] First Login Flow 정책 분기 트리 도식화 — 등급: `documented-only` (구현 안 함)
|
||
- [ ] Keycloak refresh / Google session 만료 상호작용 확인 — 등급: `needs-confirmation`
|
||
|
||
## 진행 중 메모
|
||
|
||
- Google ID token은 Keycloak이 검증한 뒤 backend로 전달하지 않는다. oauth2-proxy가 Keycloak token으로 session을 만들고, P1B backend는 [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] D3과 같은 **edge-injected header-only** 계약만 소비한다. 따라서 backend로 Google/Keycloak bearer token이 흐른다고 표현하지 않는다.
|
||
- "Google 로그인 = backend가 Google과 통신"으로 오해하기 쉬움. 다이어그램에서 Google ↔ Keycloak leg를 별도 색/박스로 강조해야 함.
|
||
- 본 sub-branch는 `documented-only` 한정 — wiki/projects/로 승급 안 함 (root TODO 참조).
|
||
|
||
## 마주친 문제
|
||
|
||
- 없음 (구현 안 함).
|
||
|
||
## 관련 sub-branch
|
||
|
||
- [[raw/branch-notes/feature-keycloak-patterns]] (root)
|
||
- [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] (P1A — Google 없는 동일 배치, 비교 기준)
|
||
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] (P2B — 내부 배치 + Google, broker 로직은 본 노트와 동일)
|
||
- [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] (P3B — 단일 EC2 + Google)
|
||
|
||
## 관련 일일 노트
|
||
|
||
|
||
## 완료 후 정리
|
||
|
||
- PR 링크: (없음, 문서까지만)
|
||
- 머지 결과 / 배포 환경: 없음 (`documented-only`)
|
||
- **wiki 추출 대상:** 없음. P1B는 root 정책상 wiki/projects/ 승급 안 함.
|
||
- **추출하지 않을 항목:** 본 sub-branch 전체 (`documented-only` / `planned`).
|