Files
llm-wiki/docs/superpowers/specs/2026-07-18-keycloak-branch-note-consistency/priority-recommendations.md
T

12 KiB

Priority Recommendations

적대 리뷰에서 살아남은 High 10건만 우선순위에 올렸다. 각 행은 하나의 finding과 1:1로 대응한다. 원문 수정은 아직 승인되지 않았다.

Priority Finding Why Now Concrete Change Verification Risk / Approval
1 L4-F08 mapper가 넣는 API audience와 backend expected audience가 다르면 정상 token이 401이 되거나 다른 resource token을 허용할 수 있다. realm client ID, API audience symbolic name, mapper target, Spring audiences 값을 한 owner D-row에서 하나로 고정하고 role-mapping의 복제 값을 pointer로 바꾼다. realm export, 발급 token aud, 정상 및 wrong-audience 401 test를 함께 확인한다. high / 개별 승인
2 L1-F08 선택된 bridge-network 기본이 localhost discovery와 host.docker.internal reachability를 동시에 요구한다. issuer identity와 JWKS network address를 분리하거나 host network를 기본으로 택해 한 실행 profile로 종결한다. docker compose config, app container DNS, startup discovery와 JWKS log를 확인한다. high / 개별 승인
3 L4-F16 copyable SPA code의 relative API와 token URL이 static-only nginx origin으로 향한다. nginx에 명시적 proxy route를 추가하거나 SPA config에 backend와 Keycloak absolute base URL을 주입하고 topology owner에서 한 방식을 선택한다. browser Network panel, nginx access log, backend와 Keycloak log로 resolved destination을 확인한다. high / 개별 승인
4 L3-F14 Caddy handle_path가 Keycloak relative path를 제거해 discovery와 authorization endpoint가 404가 될 수 있다. method B를 유지하면 handle 또는 prefix-preserving reverse_proxy로 snippet을 바꾸고, strip 방식을 택하면 Keycloak root-path 설계로 owner decision을 바꾼다. caddy adapt와 upstream request path로 discovery, auth, token endpoint를 검사한다. high / 개별 승인
5 L2-F02 silent auto-link 차단과 email_verified=false 전체 hard-reject가 같은 acceptance처럼 쓰인다. First Broker Login owner에서 silent-link policy와 custom SPI hard-reject policy를 분리하고, SPI artifact가 없으면 consumer의 링크와 생성 거부 문장을 제거한다. realm flow export, provider JAR, false-email 로그인 E2E로 선택 정책을 증명한다. high / 개별 승인
6 L4-F04 HttpOnly refresh cookie를 기본으로 두면 server-side token custody와 CSRF 책임이 생겨 SPA Direct 경계가 바뀐다. pure SPA memory-only와 TMB refresh-cookie variant를 별도 decision으로 나누고 기본 하나와 fallback 조건을 고정한다. refresh endpoint와 cookie issuer 존재, CSRF negative test, reload 및 rotation E2E를 확인한다. high / 개별 승인
7 L1-F10 P1B 표는 P1A와 동일이라 적지만 backend trust는 JWT signature와 header-only로 갈린다. P1B가 P1A trust invariant를 유지할지 새 JWT-validation variant가 될지 선택하고 pattern ID, component table, security tests를 함께 갱신한다. 두 pattern의 SecurityFilterChain, forwarded headers와 tokens, spoofing negative test를 diff한다. high / 개별 승인
8 L4-F14 Traefik note가 SPA Direct P3A를 edge-auth AP4로 연결해 배포 축과 인증 architecture 축을 섞는다. target pattern pointer를 실제 AP owner로 바꾸고 physical deployment alternative는 별도 행으로 분리한다. project pattern matrix와 component flow에서 browser token holder와 backend verifier가 일치하는지 확인한다. high / 개별 승인
9 L3-F08 random quick-tunnel URL을 static redirect hostname처럼 사용하면 restart 뒤 Google callback 등록이 stale해진다. quick tunnel과 named tunnel plus managed hostname을 분리하고 static callback 요구 시 허용되는 provider profile을 하나로 고정한다. tunnel restart 전후 hostname, DNS route, Google redirect E2E를 기록한다. high / 개별 승인
10 L1-F09 Google sub federation key의 변경 권한이 parent와 child 양쪽 SSOT에 있다. parent는 requirement만 두고 child D1을 mechanism owner로 삼거나, 반대로 parent owner를 유지하고 child를 pointer와 implementation detail로 축소한다. inbound decision reference와 owner map에서 mutable key policy owner가 하나인지 확인한다. high / 개별 승인

Decision Conditions

Finding Goal Assumption and invalidation Strongest counterargument Decision
L4-F08 API token acceptance와 resource isolation을 같은 audience contract로 묶는다. spa-client와 backend-client-id가 별도 client라는 전제다. realm export에서 동일 identifier로 의도적으로 정의됐다면 값 충돌은 사라진다. public client ID를 API audience로 재사용할 수 있다. identifier 재사용 여부를 owner에서 명시하고 mapper와 validator에 같은 symbol을 사용한다.
L1-F08 issuer identity와 container reachability가 함께 성립하는 기본을 만든다. backend가 bridge container라는 전제다. host process나 host network가 실제 기본이면 반론이 성립한다. extra_hosts로 host 접근이 가능하다. extra_hosts hostname과 issuer hostname은 자동으로 같아지지 않으므로 rendered config를 기준으로 profile을 고정한다.
L4-F16 copyable SPA request가 의도한 origin으로 간다. nginx가 static-only라는 parent 결정이 전제다. 실제 proxy route가 있으면 finding은 약해진다. relative URL은 same-origin 배포에서 단순하다. same-origin proxy를 문서와 config에 추가하거나 absolute runtime config를 쓴다.
L3-F14 proxy path와 Keycloak relative path를 같은 routing contract로 만든다. Caddy handle_path의 prefix strip과 method B가 동시에 선택됐다는 전제다. 별도 rewrite가 있으면 반론이 가능하다. upstream에서 prefix를 복구할 수 있다. 보이지 않는 rewrite에 의존하지 말고 copyable snippet에 선택을 표현한다.
L2-F02 account-link policy의 실제 거부 범위를 acceptance와 맞춘다. custom hard-reject SPI가 corpus에 없다는 전제다. 별도 artifact가 확인되면 consumer 문장을 유지할 수 있다. Google verified email이 일반적이라 edge case가 드물다. 빈도와 보안 계약은 별개이므로 flow export와 negative test로 범위를 고정한다.
L4-F04 token custody에 맞는 architecture와 CSRF contract를 선택한다. HttpOnly refresh cookie를 실제 기본으로 쓴다는 전제다. memory-only를 고르면 TMB concern이 사라진다. 기존 Resource Server에 endpoint 하나만 추가할 수 있다. endpoint 수보다 custody와 browser-triggered credential 책임을 기준으로 pattern을 분류한다.
L1-F10 P1A와 P1B의 backend trust boundary를 명시한다. 동일이라는 표가 security invariant까지 포함한다는 전제다. federation 부분만 동일하다고 범위를 좁히면 별도 variant가 가능하다. defense-in-depth JWT validation을 P1B에 추가할 수 있다. 그 경우 동일 표현을 제거하고 새 pattern invariant와 test를 둔다.
L4-F14 pattern taxonomy와 배포 alternative를 서로 다른 축으로 유지한다. P3A owner가 SPA Direct라는 전제다. project hub가 이미 AP4로 재분류했다면 owner 문서를 먼저 바꿔야 한다. 같은 EC2에 oauth2-proxy를 추가할 수 있다. 배치 가능성은 architecture ID 변경 근거가 아니므로 두 축을 분리한다.
L3-F08 redirect URI가 tunnel lifecycle 동안 안정적으로 유지되게 한다. quick tunnel URL이 random이라는 보존 source model이 전제다. target account가 persistent hostname을 제공하면 profile을 수정할 수 있다. 특정 plan이나 managed domain은 static일 수 있다. quick, named, managed custom hostname을 별도 조건으로 문서화한다.
L1-F09 federation key 변경 권한을 한 owner에 둔다. 두 D-row가 같은 mutable policy를 결정한다는 전제다. requirement와 mechanism concern ID가 분리되면 양립한다. integration parent가 requirement를 반복할 수 있다. 값과 선택 조건은 child owner에 두고 parent는 invariant pointer만 유지한다.

Fix Plan

Finding 필요한 수정 대상 파일:line 위험 승인 필요? 패치 범위
L4-F08 audience identifier와 owner 통합 raw/branch-notes/feature-keycloak-spring-rs-audience-validator.md:156, raw/branch-notes/feature-keycloak-spring-rs-role-mapping.md:38 high yes, 개별 D-row, mapper, validator, test contract
L1-F08 issuer와 network profile 한 가지 선택 raw/branch-notes/feature-keycloak-docker-compose-stack.md:138, raw/branch-notes/feature-keycloak-docker-compose-stack.md:207 high yes, 개별 Compose 및 D3 관련 문단
L4-F16 SPA endpoint routing 방식 선택 raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce.md:176, raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce.md:190, raw/branch-notes/feature-keycloak-single-ec2-no-google.md:159 high yes, 개별 code snippet과 topology decision
L3-F14 Caddy prefix-preserving snippet 적용 raw/branch-notes/feature-keycloak-reverse-proxy-headers.md:125, raw/branch-notes/feature-keycloak-reverse-proxy-headers.md:202 high yes, 개별 Caddy snippet과 D3 trace
L2-F02 silent-link와 hard-reject 분리 raw/branch-notes/feature-keycloak-first-broker-login-flow.md:135, raw/branch-notes/feature-keycloak-idp-mappers-claim-to-role.md:120, raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation.md:387 high yes, 개별 policy D-row와 consumer summaries
L4-F04 pure SPA와 TMB variant 분리 raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff.md:140, raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff.md:165 high yes, 개별 D1, architecture taxonomy, CSRF owner
L1-F10 P1B backend trust variant 확정 raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation.md:145, raw/branch-notes/feature-keycloak-edge-forwardauth-no-google.md:112 high yes, 개별 comparison table와 security invariant
L4-F14 P3A pointer를 실제 pattern owner로 교체 raw/branch-notes/feature-keycloak-traefik-forwardauth-alternative.md:169, raw/branch-notes/feature-keycloak-single-ec2-no-google.md:33 medium yes, 개별 taxonomy pointer와 설명
L3-F08 tunnel hostname 유형 분리 raw/branch-notes/feature-keycloak-public-domain-tunneling.md:69, raw/branch-notes/feature-keycloak-public-domain-tunneling.md:77, raw/branch-notes/feature-keycloak-single-ec2-google-federation.md:356 medium yes, 개별 provider matrix와 redirect assumptions
L1-F09 sub key owner 하나 선택 raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation.md:195, raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation.md:253, raw/branch-notes/feature-keycloak-account-linking-sub-vs-email.md:150 high yes, 개별 owner map와 D-row
L3-F04 bare D-reference를 wikilink plus D-id로 교체 raw/branch-notes/feature-keycloak-oauth2-proxy-oidc-flow.md:214, raw/branch-notes/feature-keycloak-oauth2-proxy-oidc-flow.md:228, raw/branch-notes/feature-keycloak-realm-client-export.md:182 및 deterministic warning 5건 low yes, 묶음 8개 reference syntax
L3-F11,L3-F12,L3-F13 rotation owner, receiver scope, Max Reuse 표현을 한 계약에서 검증 raw/branch-notes/feature-keycloak-refresh-token-rotation.md:159, raw/branch-notes/feature-keycloak-refresh-rotation-and-logout.md:124 medium yes, 묶음 D4/D5, scope, measured semantics
L4-F19,L4-F20,L4-F21 Phase C2 as-built 기준으로 history와 active spec 분리 raw/branch-notes/feature-security-operational-baseline.md:243 및 Phase C2 sections medium yes, as-built 확인 후 active table, Claims, Test Contract

Verification order

  1. Realm export와 token sample로 L4-F08을 먼저 닫는다.
  2. Rendered Compose와 proxy config로 L1-F08, L3-F14, L4-F16을 함께 검증한다.
  3. First Broker Login export와 negative E2E로 L2-F02를 확인한다.
  4. Token storage architecture 선택 뒤 L4-F04의 CSRF와 rotation owner를 정한다.
  5. 나머지 ownership 및 taxonomy cleanup을 적용하고 consistency checker를 다시 실행한다.