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

24 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-federation-spa-zero-change (P2A → P2B 전환 시 SPA 코드 변경 없음 검증) branch-note raw BR-KEYCLOAK-CHILD-26742876 branch-child keycloak-patterns-overview WI-KEYCLOAK-PATTERNS-OVERVIEW-020
DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1
1 feature-keycloak-federation-spa-zero-change feature-keycloak-patterns
keycloak-patterns
branch
keycloak-patterns
spa
idp-brokering
google-federation
p2b
2026-05-25 in-progress 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 단계.

부모 (필수)

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 code change 없이 IdP brokering을 추가하는 cross-cutting 변형으로 분류한다 raw/project-notes/keycloak-patterns-overview

브랜치 지역 결정

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

선언한 예외

Override ID Overrides Reason Approval Status

없음.

목표

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:

범위

포함 범위

  • 전 단계 (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) 확인

제외 범위

근거 (필수, 최소 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-C1C4, GOOGLE-REDIR-C1C3) + 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/endpointHTTPS 필수 · 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.
  • 다른 계약 의존:

검증해야 할 주장

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

마주친 문제

  • (학습 단계, 미실행)

묶음

본 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)