--- title: branch / feature-keycloak-google-redirect-uri-policy (P3B Google OAuth client 등록 — redirect_uri 정책 + URL 변경 시 갱신) source_type: branch-note status: raw id: BR-KEYCLOAK-CHILD-9019B40A kind: branch-child project: keycloak-patterns-overview work_item: WI-KEYCLOAK-PATTERNS-OVERVIEW-017 inherits: [DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1] refines: [] overrides: [] depends_on: [WI-KEYCLOAK-PATTERNS-OVERVIEW-015] contract_packet: 1 branch: feature-keycloak-google-redirect-uri-policy parent_branch: feature-keycloak-google-claim-attribute-mapping related_projects: [keycloak-patterns] tags: [branch, keycloak-patterns, p3b, google-oauth, idp-brokering, redirect-uri, consent-screen] created: 2026-05-25 target_merge: status_label: in-progress contract_packet_sha256: 2f73e474940f2931d53b3b512bda9bf501dd0801196d19e86bede25c36ba5656 --- # branch: feature-keycloak-google-redirect-uri-policy (P3B Google OAuth client 등록 — redirect_uri 정책 + URL 변경 시 갱신) > Layer: `raw/branch-notes/` — [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]]의 child branch. **Google Cloud Console에 OAuth 2.0 client 생성하고 redirect_uri 등록하는 절차**, 그리고 **ngrok 무료 plan URL이 변경될 때마다 Console을 갱신해야 하는 운영 burden** 학습. > 본 sub-sub-branch는 **문서까지만** — 실 Google Cloud project 생성 / OAuth client 등록 / Keycloak Admin IdP 등록은 진행하지 않음. 등급 `documented-only`. > `status_label`: `in-progress` ## 부모 (필수) [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] ## 브랜치 계약 패킷 - **생성 시 프로젝트 개정**: `1` - **패킷 스키마**: `contract_packet: 1` - **완료 조건**: Google email·name claim이 Keycloak attribute로 매핑된다 ### 상속한 프로젝트 결정 | 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 redirect URI가 Keycloak broker endpoint와 일치하도록 하는 정책에 적용한다 | [[raw/project-notes/keycloak-patterns-overview]] | ### 브랜치 지역 결정 > 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다. | Decision ID | Decision | Relation | Supporting Claims | Status | |---|---|---|---|---| ### 선언한 예외 | Override ID | Overrides | Reason | Approval | Status | |---|---|---|---|---| 없음. ## 목표 P3B의 brokering 흐름이 동작하려면 다음 3개 좌표가 글자 단위로 일치해야 한다: 1. **Google Cloud Console "Authorized redirect URIs"**에 등록된 URL 2. **Keycloak Admin → Identity Providers → Google**에서 발급하는 callback URL 3. 실제 사용자 브라우저가 Google → Keycloak으로 redirect 받을 때의 URL 이 3개가 어긋나면 Google이 `redirect_uri_mismatch` 에러로 인증 차단. 그래서 ngrok 무료 plan(URL 매 세션 변경)을 쓰면 매번 Google Console에 들어가 redirect_uri를 새 URL로 갱신해야 한다 — 이 운영 burden이 sub-sub-branch `-6-1`에서 **Cloudflare Tunnel 정적 도메인 선택**의 결정 근거. 면접에서 답해야 할 질문: 1. Keycloak callback URL의 정확한 포맷은? → `https:///realms//broker//endpoint` 2. Google OAuth client의 "Authorized JavaScript origins"는 왜 필요한가? → 본 시나리오에서는 불필요(server-to-server brokering). SPA가 Google과 직접 통신하면 필요. 3. Verification screen이 무엇이고 언제 필요한가? → basic identity scope(`openid email profile`)만 쓰는 학습 앱은 test-user allowlist·100명 상한·7일 만료 예외다. sensitive/restricted scope를 추가할 때 별도 verification 조건을 검토한다. - 이슈: - PR: ## 범위 ### 포함 범위 - Google Cloud Console OAuth 2.0 Client ID 생성 절차 (web application 타입) - Authorized JavaScript origins / Authorized redirect URIs 정책 - `client_id` + `client_secret` 발급 후 Keycloak Admin Console 입력 위치 - Verification screen (consent screen) 설정 — test users / scopes / app domain - ngrok URL 변경 시 Google Console 갱신 흐름 (수동 작업 순서) - Cloudflare Tunnel 정적 도메인이 운영 burden 감소시키는 결정 근거 정리 ### 제외 범위 - Google Workspace SAML federation (OIDC만) - Google Sign-In JS SDK 직접 사용 (Keycloak 우회 시나리오) - Google API Scopes 확장 (Gmail / Drive 등) — 본 학습은 `openid email profile`만 - 다른 OIDC Provider(GitHub / Auth0) 등록 비교 ## 근거 (필수, 최소 1개+) - [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] — Google redirect_uri 검증 규칙 공식 (D1~D4, D8 근거) - [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Google IdP 설정 가이드 (D1, D5 근거) - [[raw/official-docs/keycloak-first-login-flow]] — First Broker Login "Confirm Link Existing Account" + 자동 link 보안 경고 공식 (D7 근거 — `/branch-spec` 보강, 2026-07-16) - [[raw/official-docs/google-oauth-app-verification-state-overview-official]] — Google App Verification "OAuth app state overview": Testing/External 앱은 basic identity scope(`openid`/`email`/`profile`)만 요청하면 allowlist 없이 임의 사용자 접근 가능, verification(Published-Verified)은 sensitive/restricted scope 요청 앱에 required — D5의 "sensitive scope 회피 → verification 불필요" 부분 developer-doc 측 근거 보강 (2026-07-16) - [[raw/official-docs/google-oauth2-client-application-types-official]] — Google OAuth client Application-type 분류(Web application vs Native[Android/iOS/Desktop/UWP] vs TV & Limited-Input) + Private/Public Client 정의 공식 (D6 근거 — `wiki-source-summarizer` 보강, 2026-07-16) - [[raw/official-docs/google-oauth-manage-app-audience-official]] — Google OAuth publishing status(Testing/In production) + basic identity scope(name/email/profile) 예외 공식 (D5 verification-policy 부분 근거 — `wiki-source-summarizer` 보강, 2026-07-16) - [[raw/official-docs/google-oauth2-web-server-flow-official]] — Google "Using OAuth 2.0 for Web Server Applications" 공식 문서. confidential/server-to-server flow 정의, "Web application" application type 선택 지침, redirect URIs 요구사항 근거 (D6 근거 보강 — `wiki-source-summarizer`, 2026-07-16). 단 JavaScript origins 미언급 — D6 의 "JS origins 비움" 부분은 여전히 `UNSUPPORTED_DECISION` ## 관련 sub-branch - [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] (부모, P3B Single EC2 + Google federation) - [[raw/branch-notes/feature-keycloak-public-domain-tunneling]] — 학습 환경 public 도메인 확보 (ngrok / Cloudflare Tunnel) **← Cloudflare Tunnel 결정 근거 cross-reference** - [[raw/branch-notes/feature-keycloak-reverse-proxy-headers]] — Keycloak reverse proxy 설정 - [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]] — HTTPS termination ## TODO 각 항목 옆에 증거 등급 표기. - [ ] **Google Cloud project 생성** — `console.cloud.google.com` → New project → project name 설정 — 등급: `planned` - [ ] **OAuth consent screen 설정** — External user type / app name / support email / app logo (선택) / scopes(`openid`, `email`, `profile`). 이 basic scope 조합은 test-user 등록 불필요 — 등급: `planned` - [ ] **OAuth 2.0 Client ID 생성** — APIs & Services → Credentials → Create Credentials → OAuth client ID → Application type: **Web application** — 등급: `planned` - [ ] **Authorized JavaScript origins 입력** — 본 시나리오에서는 불필요 (Keycloak server-to-server brokering). 명시만 — 등급: `documented-only` - [ ] **Authorized redirect URIs 입력** — `https:///keycloak/realms//broker/google/endpoint` (Keycloak Admin에서 자동 생성한 callback URL 그대로 복사) — 등급: `planned` - [ ] **`client_id` + `client_secret` 발급 + Keycloak Admin 입력** — Keycloak Admin Console → Identity Providers → Add provider → Google → Client ID / Client Secret 필드 — 등급: `planned` - [ ] **Verification screen 정책 정리** — 학습용 `openid email profile`은 basic identity scope 예외라 test-user allowlist·100명 상한·7일 만료·unverified 경고가 적용되지 않는다. sensitive/restricted scope 추가 시 별도 verification 정책으로 분기 — 등급: `documented-only` - [ ] **ngrok URL 변경 시 갱신 흐름** — (a) 새 ngrok 세션 시작 → (b) 새 URL 확인 → (c) Google Console → Edit OAuth client → Authorized redirect URIs 갱신 → (d) Keycloak `KC_HOSTNAME` 환경변수 + redeploy → (e) Keycloak Admin Google IdP의 redirect URL 확인 — 등급: `planned` - [ ] **Cloudflare Tunnel 정적 도메인이 burden 제거하는 이유 정리** — 1회 등록 후 영구. 6-1과 cross-reference — 등급: `planned` ## 진행 중 메모 - Keycloak Admin Console에서 IdP alias를 `google`로 설정하면 callback URL이 `.../broker/google/endpoint` 형식으로 발급. alias를 다르게 바꾸면 그에 맞춰 URL도 변경. - Google `client_secret`은 Keycloak DB에 plaintext 저장(또는 vault credentials store) → secret rotation 정책 필요. 학습용은 무시. - Google OAuth 2.0 Client 생성 시 "Authorized JavaScript origins"는 implicit/PKCE flow의 SPA가 직접 Google과 통신할 때만 필요. 본 시나리오는 Keycloak이 server-to-server로 Google `/token` 호출 → JavaScript origins 비워둬도 동작. - Verification screen: External user type + `openid email profile`만 사용하면 basic identity scope 예외로 test-user allowlist 등록 없이 접근할 수 있다. sensitive/restricted scope를 추가할 때만 해당 verification·quota를 별도 검토한다. ### ngrok URL 변경 시 갱신 절차 (운영 burden 데모) | 단계 | 작업 | 소요 | |------|------|------| | 1 | `ngrok http 80` 재시작 → 새 URL 확인 | 즉시 | | 2 | Google Cloud Console → APIs & Services → Credentials → OAuth client 편집 | 1분 | | 3 | Authorized redirect URIs 갱신: `https://.ngrok-free.app/keycloak/realms//broker/google/endpoint` | 1분 | | 4 | Save → 변경 propagation 대기 (Google docs: 최대 수시간, 보통 즉시) | 0~수시간 | | 5 | Keycloak `KC_HOSTNAME=https://.ngrok-free.app` 갱신 후 컨테이너 재시작 | 1분 | | 6 | Keycloak Admin → Identity Providers → Google → callback URL 확인 (자동 갱신) | 즉시 | | 7 | SPA `redirect_uri`가 새 도메인을 가리키는지 확인 (vanilla JS에서는 build/run config 갱신) | 1분 | → 매 세션 5~10분 + propagation 대기. 학습 친화적 X. ### Cloudflare Tunnel 정적 도메인 대안 | 단계 | 작업 | 소요 | |------|------|------| | 1 | `cloudflared tunnel run ` 시작 → 정적 도메인 사용 | 즉시 | | 2 | Google Console redirect_uri 1회 등록 | 1분 (최초만) | | 3 | 이후 세션 변경에도 redirect_uri 갱신 불필요 | 0 | ## 결정 사항 (decisions) - **2026-05-25**: Google IdP scope는 `openid email profile`만 사용. 이유: sensitive scope 회피 → Google verification 심사 불필요 → 학습 환경에서 unverified test users로 즉시 동작. - **2026-05-25**: Google OAuth client Application type은 **Web application** 채택. 이유: Keycloak이 server-to-server로 `/token` 호출, confidential client (client_secret 사용). SPA에서 직접 Google 호출 안 함 → JavaScript origin 비워둠. - **2026-05-25 (historical, superseded)**: ~~First Broker Login Flow는 부모 P3B "마주친 문제 4번"에 따라 `email_verified=true` hard-reject까지 본 등록 노트에서 정한다.~~ - **2026-07-18**: First Broker Login 정책은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1/D4만 consume한다 — AutoLink를 추가하지 않아 silent auto-link를 차단한다. `email_verified=false` 전체 hard-reject는 구현된 custom SPI가 있는 별도 variant로 유보한다. - **2026-05-25**: ngrok 운영 burden을 정량적으로 (`매 세션 5~10분 + propagation 대기`) 기록. 이 데이터가 sub-sub-branch `-6-1`의 Cloudflare Tunnel 우선 결정의 근거가 됨. - **2026-05-25**: 본 sub-sub-branch 전체 등급 `documented-only`. 실 Google Cloud project 생성 / OAuth client 등록은 P3A 완료 후 선택적 확장 시점에 재검토. ## 결정-근거 매핑 > 본 sub-sub-branch 는 `documented-only`. cited raw sources: `google-oauth2-redirect-uri-validation-official`, `keycloak-google-idp-setup`, `keycloak-first-login-flow`(D7), `google-oauth2-client-application-types-official`+`google-oauth2-web-server-flow-official`(D6), `google-oauth-manage-app-audience-official`+`google-oauth-app-verification-state-overview-official`(D5). D5·D6·D7 은 `/branch-spec` 자동조사(2026-07-16)로 `UNSUPPORTED_DECISION` → `official-vendor-doc` 승급(단 D6 JS-origins 비움은 구조적 추론, D7 email_verified 강제·D8 정량 수치는 잔여 UNSUPPORTED). | Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk | |---|---|---|---|---| | D1 | Authorized redirect URIs 에 `https:///keycloak/realms//broker/google/endpoint` 1개만 정확히 등록 — exact match 요구 | `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C3` ("The value must exactly match one of the authorized redirect URIs ... If this value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' error") + `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C3` ("you'll need from this page is the `Redirect URI`. You'll have to provide that to Google when you register Keycloak as a client there") + `raw/official-docs/keycloak-google-idp-setup.md#KC-GIDP-C4` ("copy and paste the `Redirect URI` ... into the `Authorized redirect URIs` field") | `official-vendor-doc` | "exactly match" 의 byte-level 정의 (trailing slash / case / query string) 는 vendor verbatim 부재 — 실험 검증 필요 | | D2 | redirect URI 는 HTTPS scheme 필수 (학습 환경의 localhost 예외 제외) | `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C1` ("Redirect URIs must use the HTTPS scheme, not plain HTTP. Localhost URIs (including localhost IP address URIs) are exempt from this rule") | `official-vendor-doc` | localhost 예외가 production 시나리오 에서 허용된다는 뜻은 아님 — 학습 단계 한정 | | D3 | redirect URI host 는 raw IP 금지 — public domain 필요 → ngrok/Cloudflare Tunnel 같은 tunneling 도구 채택 정당화 | `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C2` ("Hosts cannot be raw IP addresses. Localhost IP addresses are exempted from this rule") | `official-vendor-doc` | Cloudflare Tunnel 의 `.cfargotunnel.com` 같은 generic subdomain 이 "raw IP 가 아니므로" 항상 허용되는지 vendor 정책 verbatim 부재 | | D4 | redirect URI 에 wildcard / fragment 사용 불가 → 다중 환경 (dev/staging/prod) 각각 별도 등록 | `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C4` ("Redirect URIs cannot contain the fragment component") + `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C5` ("Redirect URIs cannot contain certain characters including: Wildcard characters (`'*'`)") | `official-vendor-doc` | 환경 분리 best practice 자체는 cited raw 에 verbatim 없음 — wildcard 금지 결과로 유도된 운영 결정 | | D5 | Google IdP scope 는 `openid email profile` 만 사용 — sensitive scope 회피 → verification 심사 불필요, **그리고 이 basic identity scope 조합은 test-user allowlist 등록·100명 상한·7일 만료·unverified 경고가 모두 면제됨** (기존 본문의 "100명 test users까지" 표현은 부정확 → §마주친 문제 정합 권고 참조) | `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-oauth-manage-app-audience-official.md#GOOGLE-APPAUD-C4` ("The only exception ... userinfo.email, userinfo.profile, openid ... your users do not need to be in the trusted user list, they will not see a warning message, and their authorizations will not expire after 7 days") + `raw/official-docs/google-oauth-app-verification-state-overview-official.md#GOOGLE-VERIFY-STATE-C2` ("Exception: If the app only requests basic identity scopes (openid, email, profile), any user can access without being on the allowlist") + `#GOOGLE-VERIFY-STATE-C4` (verification 은 sensitive/restricted scope public 앱에만 "Required for") | `official-vendor-doc` | Published(In production) 전환 시에도 이 예외가 유지되는지(brand verification 별도 요구 여부)는 미확인 — `documented-only`/Testing 고정이라 당장 무영향. Testing 100-user cap(`GOOGLE-APPAUD-C1`)과 unverified-app-screen 신규 100-user cap 은 **서로 다른 quota** — 혼동 금지 | | D6 | Google OAuth client Application type = **Web application** (confidential/server-side client); Keycloak 이 server-to-server `/token` 호출 → JavaScript origins 비워둠 | Application type: `raw/official-docs/google-oauth2-web-server-flow-official.md#GOOGLE-WEBSERVER-C2` ("Select the Web application application type") + confidential flow: `#GOOGLE-WEBSERVER-C1` ("designed for applications that can store confidential information and maintain state") + `raw/official-docs/google-oauth2-client-application-types-official.md#GOOGLE-CLIENTTYPE-C1` ("Private Clients ... can securely store the client secret because they run on servers you control") + `#GOOGLE-CLIENTTYPE-C3` (web application 정의). **JS origins 비움**: `#GOOGLE-CLIENTTYPE-C5` ("Applications that use client-side JavaScript ... must specify authorized JavaScript origins") 의 *조건부 트리거* + web-server-flow 문서가 redirect URIs(`GOOGLE-WEBSERVER-C3`)만 언급하고 JS origins 미언급 → **구조적 추론** (명시적 "비워도 됨" 문장은 vendor 부재) | `official-vendor-doc (Application type/confidential 확정) + official-vendor-doc 구조적 추론 (JS origins 비움 — 명시 아님)` | Google 어떤 공식 문서도 "JavaScript origins 를 비워도 된다"를 *명시적으로* 선언 안 함 — 조건부 스코핑(C5)+web-server 문서 침묵의 추론. `verified` 승급은 실제 Console 등록 실험 후에만(§Claims To Verify). Service-account/native-app 흐름은 본 D6 범위 밖 | | D7 | **DELEGATED** — [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1/D4 — AutoLink를 추가하지 않고 소유 증명 없는 silent auto-link를 차단 | `raw/official-docs/keycloak-first-login-flow.md#KC-FLF-C2`, `#KC-FLF-C3` | `delegated + official-vendor-doc` | `email_verified=false` 전체 hard-reject는 현재 provider/SPI artifact가 없으므로 본 branch가 보장하지 않는다. 필요한 경우 별도 custom SPI variant에서 구현·검증 후 owner를 연결한다. | | D8 | ngrok 운영 burden (매 세션 5~10분 갱신) → sub-sub-branch `-6-1` 의 Cloudflare Tunnel 정적 도메인 채택 정당화 | `raw/official-docs/google-oauth2-redirect-uri-validation-official.md#GOOGLE-REDIR-C3` (exact match 요구로 인해 URL 변경 시 매번 갱신 필요) — 간접 근거. **"5~10분" 정량 수치** 는 작성자 운영 추정 (`UNSUPPORTED_DECISION` — verbatim 외부 출처 없음) | `official-vendor-doc (exact match 배경) + UNSUPPORTED_DECISION (정량 수치)` | "5~10분" 수치를 실제 측정으로 대체 (P3B 구현 시점) 또는 작성자 추정임을 명시 유지 | ## 구현 가이드 > 본 sub-sub-branch 는 `documented-only` — 여기서 "구현"은 코드가 아니라 **Google Cloud Console + Keycloak Admin 등록 절차의 사전 명세**다. 실 등록을 수행할 미래 작업자가 되묻지 않고 필드를 채울 수 있는 수준이 목표. > 3-rule: **R1** 각 row 는 Decision ID + Claim ID reference · **R2** 근거 없는 detail 은 `UNSUPPORTED_IMPL_DECISION` + trade-off 한 줄 · **R3** 본 branch 결정 범위 밖(부모 proxy/hostname 설정 · sibling flow authenticator · tunnel 설정)은 §엣지·실패·의존 으로 위임(여기 재진술 안 함). ### 1. Google Cloud Console — OAuth 2.0 Client ID 등록 필드 명세 > **Trace**: D1·D2·D3·D4 (`GOOGLE-REDIR-C1`~`C5`) redirect URI 정책 · D5 (`KC-GIDP-C5` + `GOOGLE-APPAUD-C4` + `GOOGLE-VERIFY-STATE-C2`) scope+verification · D6 (`GOOGLE-WEBSERVER-C1/C2` + `GOOGLE-CLIENTTYPE-C1/C3/C5`) client type/JS origins. > > - **UNSUPPORTED_IMPL_DECISION**: (a) `Name` 표시값 = 임의(동작 무관, trade-off: 학습 단계 무영향). (b)·(c) 는 `/branch-spec` 자동조사(2026-07-16)로 해소 — JS origins 비움은 `GOOGLE-CLIENTTYPE-C5` 조건부 트리거의 **구조적 추론**(명시적 "비워도 됨" vendor 부재 → D6 Open Risk 유지), test-user 정책은 `GOOGLE-APPAUD-C4` 예외로 **정정**(basic scope 조합엔 100명 한도 부적용). | 필드 | 입력 값 | Trace | Note | |---|---|---|---| | Application type | **Web application** | D6 (`GOOGLE-WEBSERVER-C2`, `GOOGLE-CLIENTTYPE-C1/C3`) | confidential(server-to-server) client — client_secret 서버 보관 | | Name | 임의 (예: `keycloak-broker-learning`) | — | `UNSUPPORTED_IMPL_DECISION` — 표시 이름, 동작 무관 | | Authorized redirect URIs | `https:///keycloak/realms//broker/google/endpoint` | D1 (`GOOGLE-REDIR-C3`) + `KC-GIDP-C4` | 실제 SSOT = Keycloak Admin "Redirect URI" 표시값(`KC-GIDP-C3`). `/keycloak`=부모 D4, ``/`google` alias=프로젝트 값(§엣지·실패·의존 위임) | | — scheme | HTTPS 필수 | D2 (`GOOGLE-REDIR-C1`) | localhost 만 예외 → 학습도 tunnel HTTPS 사용 | | — host | raw IP 금지 → tunnel 도메인 | D3 (`GOOGLE-REDIR-C2`) | cfargotunnel.com 통과 여부 = Claims To Verify | | — 제약 | wildcard(`*`)·fragment(`#`) 불가 | D4 (`GOOGLE-REDIR-C4`,`C5`) | dev/staging/prod 각각 별도 등록 | | Authorized JavaScript origins | (비움) | D6 (`GOOGLE-CLIENTTYPE-C5`) | client-side JS 미사용 → 구조적 추론상 불필요(명시적 vendor 문장 부재 → D6 Open Risk). `verified` 는 Console 실험 후(§Claims To Verify) | | Consent screen — User type | External | D5 | | | Consent screen — Scopes | `openid` `email` `profile` | D5 (`KC-GIDP-C5`, `GOOGLE-VERIFY-STATE-C4`) | non-sensitive → verification 회피(공식 근거 확보) | | Consent screen — Test users | (등록 불필요) | D5 (`GOOGLE-APPAUD-C4`) | ⚠️ 정정: basic scope 조합은 test-user allowlist·100명 상한·7일 만료·경고 모두 면제 — "100명 한도까지 동작" 표현은 부정확 | ### 2. Keycloak Admin Console — Google IdP 입력 매핑 > **Trace**: D1 + `KC-GIDP-C1`~`C5`. 양방향 등록(Keycloak Redirect URI → Google, Google client_id/secret → Keycloak). | 단계 | 위치 | 입력/취득 | Trace | |---|---|---|---| | IdP 추가 | Identity Providers → Add provider → **Google** | alias=`google` | `KC-GIDP-C1` | | Redirect URI 취득 | Add Identity Provider 페이지 `Redirect URI` 표시값 | §1 Authorized redirect URIs 의 SSOT — 이 값을 Google 에 복사 | `KC-GIDP-C3`,`C4` | | Client ID/Secret 입력 | 같은 페이지 `Client ID` / `Client Secret` 필드 | Google 발급값 | `KC-GIDP-C2` | | Default Scopes | Advanced → Default Scopes | `openid profile email`(기본값 유지) | `KC-GIDP-C5` | ### 3. URL 변경 시 redirect_uri 갱신 절차 > **Trace**: D8 (`GOOGLE-REDIR-C3` exact match → URL 변경 시 재등록 필수). 절차 표는 §진행 중 메모 "ngrok URL 변경 시 갱신 절차" + "Cloudflare Tunnel 정적 도메인 대안" 이 owner — Single-Owner 원칙상 **여기서 재진술하지 않는다**. tunnel 도구 채택 결정 자체는 sibling [[raw/branch-notes/feature-keycloak-public-domain-tunneling]] D1/D2 소유(§엣지·실패·의존 위임). ## 엣지·실패·의존 > R4 캡처용. 정상 등록 경로 외의 실패/엣지 + 본 branch 가 consume 하는 다른 계약. 실 적용 전이므로 "예상" 경로. **실패·엣지 경로:** - **redirect_uri exact match 위반** (D1): trailing slash 유무 / scheme 누락(http) / relative path 오타(`/keycloak` 누락) / alias 불일치 → Google `redirect_uri_mismatch` → 인증 차단. byte-level 정의(trailing slash·case·query)는 미확정 → Claims To Verify. - **propagation lag** (D1·D8): Google Console redirect_uri 변경 후 즉시~수분 지연 → 학습 시 디버깅 noise. 기대 동작: 재시도/대기. - **generic subdomain 거부 가능성** (D3): `.cfargotunnel.com` 이 "no raw IP" 정책은 통과하나 Google 이 별도 사유로 거부할 여지 → 미검증(Claims To Verify; sibling tunneling D1 의 "Does not prove" 단서와 동일 미해소). - **email auto-link 보안 위험** (D7): OOTB 기본은 Confirm Link이며 AutoLink는 별도 opt-in이다. 기대 동작: [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1/D4에 따라 AutoLink를 추가하지 않아 silent link를 차단한다. - **client_secret 노출** (진행 중 메모): Keycloak DB plaintext 저장 + docker-compose env 노출 → git 커밋 누출 위험. 기대 동작: vault/secret 관리(학습은 무시) → Claims To Verify. - **scope 확장으로 verification 조건 진입** (D5): basic identity scope에는 test-user 한도가 적용되지 않는다. sensitive/restricted scope를 추가하면 별도 verification·quota 조건으로 진입할 수 있으므로 학습 흐름은 `openid email profile`로 고정한다. **다른 계약 의존 (consume):** - [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] `D6` — broker endpoint URL 포맷(`.../broker/google/endpoint`)을 consume. 이 URL 이 곧 §구현가이드 §1 Authorized redirect URIs 값. 부모 계약 변경 시 본 branch 재등록 필요. - [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] `D4` — `KC_HOSTNAME` + `KC_HTTP_RELATIVE_PATH=/keycloak` 를 consume. redirect_uri 의 host·path 가 여기서 결정됨. proxy/hostname 설정은 부모 owner — 본 branch 는 결과 URL 만 사용. - [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1/D4 — AutoLink 미사용과 silent auto-link 차단을 consume한다. hard-reject는 본 branch 범위가 아니다. - [[raw/branch-notes/feature-keycloak-public-domain-tunneling]] D1/D2 — public 도메인 확보 수단을 consume한다. D8의 갱신 burden 비교가 이 tunnel 채택 결정에 종속하며 설정 detail은 sibling owner다. ## 검증해야 할 주장 | Claim | Why uncertain | How to verify | Status | |---|---|---|---| | Keycloak callback URL 의 정확한 path 형식 `/realms//broker//endpoint` 이 모든 Keycloak 버전에서 동일 | path 형식의 vendor verbatim 부재; `KC_HTTP_RELATIVE_PATH` 조합 시 정확한 결과의 verbatim 없음 | Keycloak 25.x docker 실행 + admin UI 의 IdP "Redirect URI" 자동 표시값 캡처 + Server Admin Guide raw 발췌 | `needs-confirmation` | | "exactly match" 의 byte-level 정의 (trailing slash / case sensitivity / query string) | cited GOOGLE-REDIR-C3 에 디테일 명시 없음 — "일반 OAuth 관례" 추정 | trailing slash 유무로 등록 후 실제 redirect 시 Google 응답 차이 실험 | `needs-confirmation` | | Cloudflare Tunnel `.cfargotunnel.com` generic subdomain 이 Google "no raw IP" 정책 통과 | cited GOOGLE-REDIR-C2 의 "raw IP 금지" 가 generic subdomain 도 cover 하는지 verbatim 부재 | Cloudflare Tunnel 정적 도메인 등록 + Google Console 등록 시도 → propagation 결과 확인 | `planned` | | ngrok URL 변경 시 Google Console propagation 시간 (vendor docs "최대 수시간") | cited raw 에 verbatim 없음 | Google Cloud Console "OAuth 2.0 settings propagation" 공식 페이지 raw 추가 | `needs-confirmation` | | unverified(Testing) app + `openid email profile` (non-sensitive scope) 만 사용 시 test-user 등록 없이 임의 Google 계정 정상 동작 (100명 한도 개념 부적용) | **공식 근거 확보**(`GOOGLE-APPAUD-C4` + `GOOGLE-VERIFY-STATE-C2` — test-user allowlist·100명·7일·경고 모두 면제). 잔여 불확실 = Published(In production) 전환 시 brand verification 별도 요구 여부만 | 실제 Testing app 으로 등록 후 임의 Google 계정 로그인 동작 실측 (문서 근거는 완료) | `needs-confirmation (문서 근거 확보, 실측 미실시)` | | Keycloak `client_secret` plaintext 저장 (또는 vault credentials store) 동작 — git 커밋 누출 위험 | cited raw 에 verbatim 없음 | Keycloak Server Admin Guide "Vault" 섹션 raw 추가 + Keycloak DB 의 `client_secret` 컬럼 확인 | `needs-confirmation` | | `Authorized JavaScript origins` 가 server-to-server brokering 시나리오에서 정말 비워둘 수 있음 | **구조적 근거 확보**(`GOOGLE-CLIENTTYPE-C5` 조건부 트리거 "client-side JS 사용 시에만 필수" + `GOOGLE-WEBSERVER` 문서의 JS origins 미언급) — 단 "비워도 됨" **명시 문장은 vendor 부재**(추론) | origins 비운 상태로 Keycloak ↔ Google `/token` 호출 정상 동작 실측 (구조적 근거는 완료) | `needs-confirmation (구조적 근거 확보, 실측 미실시)` | ## 마주친 문제 - 아직 없음 (문서 단계). 실 적용 시 예상되는 함정: - **redirect_uri exact match 위반**: trailing slash 유무 / scheme 누락 / relative path 오타. Google docs는 "must match exactly". - **propagation lag**: Google 측 redirect_uri 변경 후 즉시 반영되지만 캐시 영향으로 수분 지연 사례 보고됨. 학습 시 디버깅 noise. - **First Broker Login email match AutoLink**: OOTB 기본이 아니라 별도 opt-in이며, 활성화하면 보안 위험이 생긴다. 현재 정책은 [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] D1에 따라 추가하지 않는다. - **`client_secret` 노출**: Keycloak DB에 plaintext 저장. Git 커밋 / docker-compose env file 노출 위험. > **정합 권고 (`/branch-spec` 자동조사 2026-07-16 — 사용자 본문 verbatim 미변경, 정정만 surface):** > **정합 반영 완료 (2026-07-18)**: `목표/WHY`·`TODO`·`진행 중 메모`의 옛 "100명 test users까지" 문구를 basic identity scope 예외로 갱신했다. `openid`/`email`/`profile`만 요청하면 test-user allowlist·100명 상한·7일 만료·unverified 경고가 면제된다(`GOOGLE-APPAUD-C4`, `GOOGLE-VERIFY-STATE-C2`). sensitive/restricted scope의 quota는 별도 조건이다. ## 묶음 - [[raw/official-docs/google-oauth-app-verification-state-overview-official]] - [[raw/official-docs/google-oauth-manage-app-audience-official]] - [[raw/official-docs/google-oauth2-client-application-types-official]] - [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] - [[raw/official-docs/google-oauth2-web-server-flow-official]] - [[raw/official-docs/keycloak-google-idp-setup]] - [[raw/official-docs/keycloak-identity-brokering-overview-official]] - [[raw/official-docs/oauth-v2-1-draft-ietf]] > 본 branch 는 leaf — 자식 자료 없음. errors / interview prep / lectures 가 누적되면 본 섹션에서 그룹화. ### 근거 자료 > 전체 목록·정당화 결정 매핑은 상단 "## Sources / 근거" 섹션이 owner (Single-Owner, 중복 재진술 안 함). 최근 추가: [[raw/official-docs/google-oauth-app-verification-state-overview-official]] — D5 verification-policy 부분 developer-doc 근거 보강 (2026-07-16). ### 오류 기록 - (없음) ### 면접 준비 - (없음) ## 관련 일일 노트 ## 완료 후 정리 > 본 sub-sub-branch는 **문서까지만**. 실 Google Cloud project / OAuth client 등록은 P3A 완료 후 선택적 확장. - PR 링크: - 리뷰 메모: - 머지 결과 / 배포 환경: 해당 없음 (문서 단계, P3B 전체 `documented-only`) - **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출): - `actually-implemented` 항목: 없음 - `locally-verified` 항목: 없음 - `prod-verified` 항목: 없음 - **추출하지 않을 항목** (planned / documented-only / abandoned): - 본 sub-sub-branch 전체가 `documented-only`. 추후 `wiki/concepts/keycloak-deployment-patterns.md` 합성 시 "Google IdP 등록 + redirect_uri exact match" 섹션으로 인용 후보.