197 lines
12 KiB
Markdown
197 lines
12 KiB
Markdown
---
|
|
title: branch / feature-keycloak-idp-mappers-claim-to-role (IdP Mappers — Google claim → Keycloak role)
|
|
source_type: branch-note
|
|
status: raw
|
|
id: BR-KEYCLOAK-CHILD-321B472C
|
|
kind: branch-child
|
|
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-mappers-claim-to-role
|
|
parent_branch: feature-keycloak-idp-brokering-google-client
|
|
related_projects: [keycloak-patterns]
|
|
tags: [branch, keycloak-patterns, spa, idp-brokering, google-federation, idp-mappers, p2b]
|
|
created: 2026-05-25
|
|
target_merge:
|
|
status_label: in-progress
|
|
contract_packet_sha256: 6c7b7381db7953e7ba85ecf9dfc998cce5e018e0f146a3caa4a34e978c45324a
|
|
---
|
|
|
|
# branch: feature-keycloak-idp-mappers-claim-to-role (IdP Mappers — Google claim → Keycloak role)
|
|
|
|
> Layer: `raw/branch-notes/` — [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]]의 WI015 child branch.
|
|
> 학습 노트. P2B는 `documented-only` 단계.
|
|
|
|
> **정합 노트 (2026-07-14 감사)**: 본 노트의 attribute-mapping 내용(Attribute Importer / Sync Mode / `hd` / `email_verified`)은 형제 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] 와 ~70% 겹친다 — 그쪽이 attribute-mapping **owner**. 본 노트의 고유 책임 = **claim → role (RBAC 인가)** 이며 §5 **deferred authZ 트랙**. /ingest 시 attribute 부분은 owner 를 인용하고 본 노트는 role 부분만 남긴다.
|
|
|
|
<!-- section-id: branch-parent -->
|
|
## 부모 (필수)
|
|
|
|
[[raw/branch-notes/feature-keycloak-idp-brokering-google-client]]
|
|
|
|
<!-- 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 claim-to-role mapper를 IdP brokering 구성의 하위 계약으로 적용한다 | [[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 -->
|
|
|
|
## 묶음
|
|
|
|
<!-- GENERATED: sources:start -->
|
|
- [[raw/official-docs/keycloak-identity-provider-mappers]]
|
|
<!-- GENERATED: sources:end -->
|
|
|
|
> 본 sub-sub-branch 는 leaf — 자식 자료 없음. Phase 3 P3A 실 구현 또는 외부 산출물 단계에서 errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화.
|
|
|
|
### 오류 기록 (이 sub-sub-branch 작업 중 발생)
|
|
|
|
- (없음 — 현재 documented-only 단계)
|
|
|
|
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
|
|
|
|
- (없음 — Phase 3 실 구현 단계에 누적)
|
|
|
|
<!-- section-id: branch-goal -->
|
|
## 목표
|
|
|
|
Google ID token claim을 Keycloak role로 매핑해서, **SPA / backend가 Google 출신 사용자를 Keycloak local 사용자와 동일 RBAC 모델에서** 다룰 수 있게 한다. Profile attribute 매핑은 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]]이 정본이다.
|
|
|
|
면접 질문: "Google에서 받은 사용자 정보를 backend가 어떻게 보나요?"
|
|
→ "Keycloak의 IdP role mapper로 Google claim(예: `hd`)을 role로 변환합니다. Profile attribute는 별도 owner가 매핑하고, backend는 최종 Keycloak access token의 role만 소비합니다."
|
|
|
|
- 이슈:
|
|
- PR:
|
|
|
|
<!-- section-id: branch-scope -->
|
|
## 범위
|
|
|
|
### 포함 범위
|
|
|
|
- **Hardcoded Role / Claim to Role / Advanced Claim to Role**: Google claim 값에 따른 role 부여.
|
|
- Google `hd` claim 기반 role 분기와 개인 Gmail(`hd` 부재) 처리 정책.
|
|
- **role mapper instance-level `Sync Mode Override = FORCE`**: IdP-level default를 바꾸지 않고 role freshness만 갱신(D1).
|
|
|
|
### 제외 범위
|
|
|
|
- Google profile claim → user attribute, Username Template, picture 전파, IdP-level default Sync Mode → [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D2/D3/D4.
|
|
- `email_verified=false` link 정책 → [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4. 현재 범위는 silent auto-link 차단이며 hard-reject SPI는 별도 variant다.
|
|
- 코드 변경 없음 검증 — [[raw/branch-notes/feature-keycloak-federation-spa-zero-change]]
|
|
- JWT signature 검증 메커니즘 — [[raw/branch-notes/feature-keycloak-three-leg-trust-chain]]
|
|
- Account Linking 흐름 — [[raw/branch-notes/feature-keycloak-account-linking-spa-ux]]
|
|
|
|
## TODO
|
|
|
|
- [ ] Role mapper 종류표 작성 — Hardcoded Role / Claim to Role / Advanced Claim to Role — 등급: `documented-only`
|
|
- [ ] Google `hd` claim → Advanced Claim to Role mapper 설정 (예: `hd=example.com`이면 `internal-user` role 부여) — 등급: `documented-only`
|
|
- [ ] IdP-level default `IMPORT`를 유지하면서 **role mapper instance에만 `Sync Mode Override=FORCE`** 설정 — 등급: `planned`
|
|
- [ ] `hd` 미존재 시 (개인 Gmail 계정) 처리 정책 — 등급: `planned`
|
|
|
|
## 진행 중 메모
|
|
|
|
- Profile attribute와 token claim 전파는 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4 — 본 노트는 그 값을 재명세하지 않는다.
|
|
- `hd` claim은 **Google Workspace 계정에만 존재**. 개인 Gmail 계정은 `hd` 없음. 따라서 "hd 없으면 거부"는 **사내 SaaS** 용도 (학습 노트에선 미적용).
|
|
- IdP-level default는 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3의 `IMPORT`다. 본 노트의 `FORCE`는 **role mapper instance-level override**로만 적용한다.
|
|
|
|
## 결정 사항 (decisions)
|
|
|
|
- 2026-07-18: **role mapper instance-level `Sync Mode Override = FORCE`** — role freshness만 갱신한다. IdP-level default는 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3의 `IMPORT`를 유지한다.
|
|
- 2026-07-18: `email_verified=false` 정책은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4를 consume한다. 현재 custom SPI artifact가 없으므로 전체 hard-reject를 이 branch에서 주장하지 않는다.
|
|
- 2026-05-25: **`hd` claim 미적용** — 학습 단계는 개인 Gmail도 허용. 운영 SaaS 도입 시 Advanced Claim to Role로 `hd=example.com → internal-user` 매핑 추가.
|
|
- 2026-05-25 (delegated): `picture` 전파는 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4가 소유한다.
|
|
|
|
## 마주친 문제
|
|
|
|
- (학습 단계, 미실행)
|
|
|
|
## 관련 일일 노트
|
|
|
|
|
|
## 근거 (필수, 최소 1개+)
|
|
|
|
- [[raw/official-docs/keycloak-identity-provider-mappers]]
|
|
- [[raw/official-docs/keycloak-identity-provider-sync-mode-official]]
|
|
- [[raw/official-docs/google-oidc-discovery-spec]]
|
|
|
|
## 결정-근거 매핑
|
|
|
|
> 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. `Decision ID` 는 본 branch-note 안에서 안정적으로 유지.
|
|
|
|
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|
|
|---|---|---|---|---|
|
|
| D1 | **role mapper instance-level `Sync Mode Override = FORCE`** — IdP-level default `IMPORT`와 적용 계층을 분리 | `raw/official-docs/keycloak-identity-provider-sync-mode-official.md#KC-SYNCMODE-C4` (`force` = each login update), `#KC-SYNCMODE-C5` (mapper-level override) + [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3 — IdP-level default `IMPORT` | `official-vendor-doc + delegated` | role mapper에서 override가 노출되고 IdP default보다 우선하는지는 realm export/Admin UI 실측 전까지 `needs-confirmation` |
|
|
| D2 | **DELEGATED** — [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 — 현재 정책은 silent auto-link 차단 | owner D4 | `delegated` | `email_verified=false` 전체 hard-reject는 구현된 custom SPI가 있는 별도 variant에서만 가능하며 현재 artifact 없음 |
|
|
| D3 | `hd` claim 미적용 (학습 단계 — 개인 Gmail 허용, 운영 SaaS 전환 시 Advanced Claim to Role 매핑 추가) | `raw/official-docs/google-oidc-discovery-spec.md#GOOGLE-OIDC-C7` (`hd` 는 Workspace/Cloud organization 도메인 — personal Google account 의 `hd` 부재는 인용에 명시 없음 단서) | `official-vendor-doc` | personal Gmail 의 `hd` claim 부재 시 Keycloak mapper 동작 (null / 없음 / 거부) 의 정확한 검증은 별도 필요 — `GOOGLE-OIDC-C7` "Does not prove" 단서 |
|
|
| D4 | **DELEGATED** — [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D4 — profile attribute 매핑 owner | owner D4 | `delegated` | 본 role branch에서 attribute/token 전파 값을 재명세하지 않음 |
|
|
|
|
## 구현 가이드
|
|
|
|
| 단계 | 이 branch의 설정 | 완료 조건 |
|
|
|---|---|---|
|
|
| 1 | IdP-level default는 [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3의 `IMPORT`를 그대로 사용 | realm export에서 IdP 기본값이 `IMPORT`임을 확인 |
|
|
| 2 | Google IdP 아래 role mapper instance에만 `Sync Mode Override=FORCE`를 적용 | realm export에서 해당 mapper의 override만 `FORCE`임을 확인 |
|
|
| 3 | 운영 SaaS variant에서만 `hd=example.com` 조건의 Advanced Claim to Role mapper를 추가 | Workspace 계정과 개인 계정의 최종 Keycloak role 차이를 token으로 검증 |
|
|
|
|
현재는 `documented-only`다. Admin UI 캡처, realm export, 로그인 2회 후 role 갱신 증거가 모이기 전에는 구현 완료로 승격하지 않는다.
|
|
|
|
## 엣지·실패·의존
|
|
|
|
| 구분 | 조건 | 처리 / owner |
|
|
|---|---|---|
|
|
| Edge | 개인 Google 계정에 `hd`가 없을 수 있음 | 학습 variant에서는 로그인 자체를 거부하지 않고 도메인 기반 role만 부여하지 않는 정책을 검증한다 |
|
|
| Failure | `FORCE`를 IdP-level default로 잘못 적용 | profile attribute까지 매 로그인 갱신되는 범위 확장을 피하고, role mapper instance override로 되돌린다 |
|
|
| Dependency | profile attribute와 IdP default Sync Mode | [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] D3/D4 |
|
|
| Dependency | `email_verified=false` 연결 정책 | [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D4 |
|
|
|
|
## 검증해야 할 주장
|
|
|
|
> 공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다.
|
|
|
|
| Claim | Why uncertain | How to verify | Status |
|
|
|---|---|---|---|
|
|
| role mapper instance-level `Sync Mode Override=FORCE`가 IdP default `IMPORT`보다 우선하는지 | 적용 계층은 공식 근거가 있으나 배포 버전 realm export/UI 실측 없음 | role mapper만 FORCE로 설정하고 2회 로그인 후 role 갱신과 profile attribute 보존을 함께 확인 | `needs-confirmation` |
|
|
| Advanced Claim to Role mapper 가 `hd=example.com` 일 때만 `internal-user` role 부여 (운영 SaaS 시) | `KC-IDP-MAPPER-C4` 가 명시적으로 `needs-confirmation` — mapper 종류 verbatim 부재 | Keycloak Admin UI 의 IdP → Mappers → Add mapper → Advanced Claim to Role 캡쳐 + 실제 등록 후 `hd` 별 token 발급 → role 차이 확인 | `planned` |
|
|
| `email_verified=false` 전체 hard-reject variant | 현재 custom SPI provider/JAR/flow export가 없음 | 별도 SPI branch를 만들 때 provider artifact + realm flow export + negative E2E로 검증 | `deferred` |
|
|
|
|
## 완료 후 정리
|
|
|
|
> 학습 노트. P2B는 `documented-only` 유지.
|
|
|
|
- PR 링크:
|
|
- 리뷰 메모:
|
|
- 머지 결과 / 배포 환경: 학습 노트 (`documented-only`)
|
|
- **wiki 추출 대상**:
|
|
- `actually-implemented` 항목: (없음)
|
|
- `locally-verified` 항목: (없음)
|
|
- `prod-verified` 항목: (없음)
|
|
- **추출하지 않을 항목**: 전 항목 (`documented-only`)
|