diff --git a/docs/keycloak/final/deterministic-lint.md b/docs/keycloak/final/deterministic-lint.md deleted file mode 100644 index 51a7975..0000000 --- a/docs/keycloak/final/deterministic-lint.md +++ /dev/null @@ -1,7 +0,0 @@ -# Deterministic lint report - -- Score: **100.0/100** -- Word count: **9043** -- Issues: **0** - -No issues found. diff --git a/docs/keycloak/final/evidence-map.json b/docs/keycloak/final/evidence-map.json deleted file mode 100644 index 20b6ed3..0000000 --- a/docs/keycloak/final/evidence-map.json +++ /dev/null @@ -1,2049 +0,0 @@ -{ - "schema_version": 2, - "document": "브라우저 토큰에서 엣지 세션까지: Keycloak 인증 패턴 네 가지의 경계 설계", - "citation_style": "hidden", - "reader_document_contains_internal_source_ids": false, - "sections": [ - { - "section_id": "01-problem-scene", - "intent": "problem_scene", - "title": "코드보다 먼저 드러난 문제", - "reader_question": "독자가 공감할 수 있는 구체적인 상황에서 어떤 문제가 드러났는가?", - "decision_requirements": [], - "evidence": [ - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "AP1_BOUNDARY", - "title": "AP1 SPA direct의 OAuth·token 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/internal-spa-direct-no-google.md", - "heading": "AP1 internal SPA direct: local identity profile", - "line_start": 3, - "line_end": 10, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md", - "accessed": "", - "claim_ids": [ - "AP1-C1" - ], - "decision_ids": [ - "AP1-D1" - ], - "priority": 100.0 - }, - { - "id": "AP2_BOUNDARY", - "title": "AP2 confidential token mediator의 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap2-token-boundary.md", - "heading": "책임 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md", - "accessed": "", - "claim_ids": [ - "AP2-C1" - ], - "decision_ids": [ - "AP2-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_BOUNDARY", - "title": "AP3 BFF의 tokenless browser 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap3-bff-boundary.md", - "heading": "요청과 token 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md", - "accessed": "", - "claim_ids": [ - "AP3-C1" - ], - "decision_ids": [ - "AP3-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "02-constraints", - "intent": "constraints", - "title": "문제를 어렵게 만든 제약", - "reader_question": "단순한 해법을 막은 프로젝트 제약은 무엇이었는가?", - "decision_requirements": [], - "evidence": [ - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "AP1_STORAGE", - "title": "AP1 token 저장 선택과 수용 비용", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap1-token-storage.md", - "heading": "AP1 token storage trade-off", - "line_start": 3, - "line_end": 29, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md", - "accessed": "", - "claim_ids": [ - "AP1-C2" - ], - "decision_ids": [ - "AP1-D2" - ], - "priority": 100.0 - }, - { - "id": "AP2_GUARDRAILS", - "title": "AP2 session, refresh custody, CORS와 audience 가드레일", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/resources/application.yml", - "heading": "AP2 session and OAuth client configuration", - "line_start": 3, - "line_end": 34, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/application.yml", - "accessed": "", - "claim_ids": [ - "AP2-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP3_TRADEOFF", - "title": "AP3와 AP1의 위협 모델·운영비 교환", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/bff-vs-spa-direct.md", - "heading": "BFF vs SPA direct", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md", - "accessed": "", - "claim_ids": [ - "AP3-C2" - ], - "decision_ids": [ - "AP3-D2" - ], - "priority": 100.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_RESPONSE_RUNTIME", - "title": "AP4 trusted header input에서 edge identity JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "heading": "currentUser and hasValidInternalToken", - "line_start": 27, - "line_end": 53, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "accessed": "", - "claim_ids": [ - "AP4-C7" - ], - "decision_ids": [], - "priority": 100.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "03-options", - "intent": "options", - "title": "검토한 선택지와 막힌 지점", - "reader_question": "어떤 대안들을 검토했고 각각 어디에서 비용이 생겼는가?", - "decision_requirements": [ - "상황·제약", - "선택", - "선택 이유", - "검토한 대안", - "수용한 비용", - "보완 가드레일" - ], - "evidence": [ - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "AP1_BOUNDARY", - "title": "AP1 SPA direct의 OAuth·token 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/internal-spa-direct-no-google.md", - "heading": "AP1 internal SPA direct: local identity profile", - "line_start": 3, - "line_end": 10, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md", - "accessed": "", - "claim_ids": [ - "AP1-C1" - ], - "decision_ids": [ - "AP1-D1" - ], - "priority": 100.0 - }, - { - "id": "AP1_STORAGE", - "title": "AP1 token 저장 선택과 수용 비용", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap1-token-storage.md", - "heading": "AP1 token storage trade-off", - "line_start": 3, - "line_end": 29, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md", - "accessed": "", - "claim_ids": [ - "AP1-C2" - ], - "decision_ids": [ - "AP1-D2" - ], - "priority": 100.0 - }, - { - "id": "AP2_BOUNDARY", - "title": "AP2 confidential token mediator의 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap2-token-boundary.md", - "heading": "책임 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md", - "accessed": "", - "claim_ids": [ - "AP2-C1" - ], - "decision_ids": [ - "AP2-D1" - ], - "priority": 100.0 - }, - { - "id": "AP2_IMPLEMENTATION", - "title": "AP2 access-only handoff의 실제 구현", - "source_type": "canonical-project", - "status": "reviewed-discrepancy", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "heading": "accessToken", - "line_start": 28, - "line_end": 55, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "accessed": "", - "claim_ids": [ - "AP2-C2" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_BOUNDARY", - "title": "AP3 BFF의 tokenless browser 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap3-bff-boundary.md", - "heading": "요청과 token 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md", - "accessed": "", - "claim_ids": [ - "AP3-C1" - ], - "decision_ids": [ - "AP3-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_TRADEOFF", - "title": "AP3와 AP1의 위협 모델·운영비 교환", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/bff-vs-spa-direct.md", - "heading": "BFF vs SPA direct", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md", - "accessed": "", - "claim_ids": [ - "AP3-C2" - ], - "decision_ids": [ - "AP3-D2" - ], - "priority": 100.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_ALTERNATIVE", - "title": "AP4 Traefik ForwardAuth 대안과 추가 비용", - "source_type": "branch-note", - "status": "config-tested", - "path": "docs/traefik-forwardauth-alternative.md", - "heading": "Traefik ForwardAuth alternative", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/traefik-forwardauth-alternative.md", - "accessed": "", - "claim_ids": [ - "AP4-C4" - ], - "decision_ids": [ - "AP4-D2" - ], - "priority": 85.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "04-decision-rationale", - "intent": "decision_rationale", - "title": "선택의 이유와 지킨 경계", - "reader_question": "왜 이 선택을 했으며 무엇을 일부러 포기하거나 금지했는가?", - "decision_requirements": [ - "상황·제약", - "선택", - "선택 이유", - "검토한 대안", - "수용한 비용", - "보완 가드레일" - ], - "evidence": [ - { - "id": "AP1_BOUNDARY", - "title": "AP1 SPA direct의 OAuth·token 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/internal-spa-direct-no-google.md", - "heading": "AP1 internal SPA direct: local identity profile", - "line_start": 3, - "line_end": 10, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md", - "accessed": "", - "claim_ids": [ - "AP1-C1" - ], - "decision_ids": [ - "AP1-D1" - ], - "priority": 100.0 - }, - { - "id": "AP1_STORAGE", - "title": "AP1 token 저장 선택과 수용 비용", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap1-token-storage.md", - "heading": "AP1 token storage trade-off", - "line_start": 3, - "line_end": 29, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md", - "accessed": "", - "claim_ids": [ - "AP1-C2" - ], - "decision_ids": [ - "AP1-D2" - ], - "priority": 100.0 - }, - { - "id": "AP2_BOUNDARY", - "title": "AP2 confidential token mediator의 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap2-token-boundary.md", - "heading": "책임 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md", - "accessed": "", - "claim_ids": [ - "AP2-C1" - ], - "decision_ids": [ - "AP2-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_BOUNDARY", - "title": "AP3 BFF의 tokenless browser 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap3-bff-boundary.md", - "heading": "요청과 token 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md", - "accessed": "", - "claim_ids": [ - "AP3-C1" - ], - "decision_ids": [ - "AP3-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_TRADEOFF", - "title": "AP3와 AP1의 위협 모델·운영비 교환", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/bff-vs-spa-direct.md", - "heading": "BFF vs SPA direct", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md", - "accessed": "", - "claim_ids": [ - "AP3-C2" - ], - "decision_ids": [ - "AP3-D2" - ], - "priority": 100.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_ALTERNATIVE", - "title": "AP4 Traefik ForwardAuth 대안과 추가 비용", - "source_type": "branch-note", - "status": "config-tested", - "path": "docs/traefik-forwardauth-alternative.md", - "heading": "Traefik ForwardAuth alternative", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/traefik-forwardauth-alternative.md", - "accessed": "", - "claim_ids": [ - "AP4-C4" - ], - "decision_ids": [ - "AP4-D2" - ], - "priority": 85.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "05-mechanism", - "intent": "mechanism", - "title": "선택이 코드와 흐름에 반영되는 방식", - "reader_question": "결정이 모듈, 인터페이스, 제어 흐름에 어떻게 반영되는가?", - "decision_requirements": [], - "evidence": [ - { - "id": "AP1_LOGIN_RUNTIME", - "title": "AP1 SPA authorization, callback와 browser token data flow", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/src/app.js", - "heading": "UserManager configuration, callback, and renderSession", - "line_start": 9, - "line_end": 81, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js", - "accessed": "", - "claim_ids": [ - "AP1-C4" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP1_PKCE_DEMO_GAP", - "title": "AP1 manual PKCE helper와 실제 signin path의 구분", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "frontend/src/pkce.js", - "heading": "createPkcePair", - "line_start": 1, - "line_end": 25, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/pkce.js", - "accessed": "", - "claim_ids": [ - "AP1-C5" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP1_API_RUNTIME", - "title": "AP1 browser Bearer input에서 /api/me JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/src/app.js", - "heading": "callProtectedApi", - "line_start": 83, - "line_end": 109, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js", - "accessed": "", - "claim_ids": [ - "AP1-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP1_ROLE_FAILURE_RUNTIME", - "title": "AP1 JWT failure와 realm role authorization 경계", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "heading": "convert", - "line_start": 12, - "line_end": 27, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "accessed": "", - "claim_ids": [ - "AP1-C7" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP2_LOGIN_FLOW", - "title": "AP2 browser entry와 Spring oauth2Login code 교환", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/resources/static/app.js", - "heading": "loginButton click and OAuth client registration", - "line_start": 7, - "line_end": 9, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/static/app.js", - "accessed": "", - "claim_ids": [ - "AP2-C4" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP2_BOUNDARY_RUNTIME", - "title": "AP2 token boundary endpoint input과 output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java", - "heading": "tokenBoundary", - "line_start": 25, - "line_end": 42, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java", - "accessed": "", - "claim_ids": [ - "AP2-C5" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP2_ACCESS_RUNTIME", - "title": "AP2 access handoff와 browser direct API의 data transformation", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "heading": "accessToken", - "line_start": 28, - "line_end": 54, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "accessed": "", - "claim_ids": [ - "AP2-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP2_RESOURCE_RUNTIME", - "title": "AP2 Resource Server의 JWT input과 /api/me output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/ApiController.java", - "heading": "currentUser", - "line_start": 21, - "line_end": 28, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/backend/src/main/java/com/example/keycloakpattern/ApiController.java", - "accessed": "", - "claim_ids": [ - "AP2-C7" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP3_LOGIN_FLOW", - "title": "AP3 oauth2Login과 server-side PKCE data flow", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "heading": "authorizationRequestResolver and bffSecurity", - "line_start": 20, - "line_end": 58, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "accessed": "", - "claim_ids": [ - "AP3-C4" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP3_BOUNDARY_RUNTIME", - "title": "AP3 token boundary endpoint의 input과 관측 output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "tokenBoundary", - "line_start": 44, - "line_end": 64, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C5" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP3_API_RUNTIME", - "title": "AP3 session input에서 downstream Bearer와 reader JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "currentUser and authorizedClient", - "line_start": 67, - "line_end": 111, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_CSRF_RUNTIME", - "title": "AP3 CSRF cookie-to-header transformation과 preferences output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "heading": "csrf", - "line_start": 14, - "line_end": 23, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "accessed": "", - "claim_ids": [ - "AP3-C7" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_PREFERENCE_SCOPE", - "title": "AP3 preferences 예시의 process-global state 간극", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "preferenceTheme and updatePreferences", - "line_start": 28, - "line_end": 95, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C8" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP4_LOGIN_RUNTIME", - "title": "AP4 unauthenticated navigation에서 oauth2-proxy session까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "docker-compose.yml", - "heading": "oauth2-proxy service", - "line_start": 92, - "line_end": 143, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docker-compose.yml", - "accessed": "", - "claim_ids": [ - "AP4-C5" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_REQUEST_RUNTIME", - "title": "AP4 external request에서 auth subrequest와 upstream input까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/default.conf.template", - "heading": "auth_request and upstream mapping", - "line_start": 13, - "line_end": 74, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/frontend/default.conf.template", - "accessed": "", - "claim_ids": [ - "AP4-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_RESPONSE_RUNTIME", - "title": "AP4 trusted header input에서 edge identity JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "heading": "currentUser and hasValidInternalToken", - "line_start": 27, - "line_end": 53, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "accessed": "", - "claim_ids": [ - "AP4-C7" - ], - "decision_ids": [], - "priority": 100.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "06-evidence-verification", - "intent": "evidence_verification", - "title": "결정이 지켜지는지 확인하는 방법", - "reader_question": "설명한 경계와 결과가 실제로 유지되는지 어떻게 확인하는가?", - "decision_requirements": [], - "evidence": [ - { - "id": "AP1_VERIFY", - "title": "AP1 브라우저 흐름과 token 수명주기 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern1.mjs", - "heading": "AP1 Playwright acceptance contract", - "line_start": 82, - "line_end": 202, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/e2e/pattern1.mjs", - "accessed": "", - "claim_ids": [ - "AP1-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP1_ROLE_FAILURE_RUNTIME", - "title": "AP1 JWT failure와 realm role authorization 경계", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "heading": "convert", - "line_start": 12, - "line_end": 27, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "accessed": "", - "claim_ids": [ - "AP1-C7" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP2_VERIFY", - "title": "AP2 access-only 전달과 브라우저 직접 API 호출 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern2.mjs", - "heading": "AP2 Playwright acceptance contract", - "line_start": 39, - "line_end": 112, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/e2e/pattern2.mjs", - "accessed": "", - "claim_ids": [ - "AP2-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP2_ACCESS_RUNTIME", - "title": "AP2 access handoff와 browser direct API의 data transformation", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "heading": "accessToken", - "line_start": 28, - "line_end": 54, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "accessed": "", - "claim_ids": [ - "AP2-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_VERIFY", - "title": "AP3 browser token 비노출과 CSRF 방어 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern3.mjs", - "heading": "AP3 Playwright acceptance contract", - "line_start": 43, - "line_end": 191, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/e2e/pattern3.mjs", - "accessed": "", - "claim_ids": [ - "AP3-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP3_CSRF_RUNTIME", - "title": "AP3 CSRF cookie-to-header transformation과 preferences output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "heading": "csrf", - "line_start": 14, - "line_end": 23, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "accessed": "", - "claim_ids": [ - "AP3-C7" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_VERIFY", - "title": "AP4 edge login과 header spoofing 방어 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern4.mjs", - "heading": "AP4 Playwright acceptance contract", - "line_start": 44, - "line_end": 131, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/e2e/pattern4.mjs", - "accessed": "", - "claim_ids": [ - "AP4-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP4_RESPONSE_RUNTIME", - "title": "AP4 trusted header input에서 edge identity JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "heading": "currentUser and hasValidInternalToken", - "line_start": 27, - "line_end": 53, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "accessed": "", - "claim_ids": [ - "AP4-C7" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "BRANCH_REACHABILITY", - "title": "네 pattern branch와 39개 feature ref의 도달성", - "source_type": "canonical-project", - "status": "partially-verified", - "path": "docs/keycloak-branch-manifest.tsv", - "heading": "branch target delivery registry", - "line_start": 1, - "line_end": 40, - "url": "repo://keycloak-pattern/develop/docs/keycloak-branch-manifest.tsv", - "accessed": "", - "claim_ids": [ - "COMMON-T1" - ], - "decision_ids": [], - "priority": 70.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "07-tradeoffs", - "intent": "tradeoffs", - "title": "얻은 것, 잃은 것, 적용하지 않을 때", - "reader_question": "이 선택의 비용과 한계는 무엇이며 언제 다른 선택이 나은가?", - "decision_requirements": [ - "상황·제약", - "선택", - "선택 이유", - "검토한 대안", - "수용한 비용", - "보완 가드레일" - ], - "evidence": [ - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "AP1_STORAGE", - "title": "AP1 token 저장 선택과 수용 비용", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap1-token-storage.md", - "heading": "AP1 token storage trade-off", - "line_start": 3, - "line_end": 29, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md", - "accessed": "", - "claim_ids": [ - "AP1-C2" - ], - "decision_ids": [ - "AP1-D2" - ], - "priority": 100.0 - }, - { - "id": "AP2_GUARDRAILS", - "title": "AP2 session, refresh custody, CORS와 audience 가드레일", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/resources/application.yml", - "heading": "AP2 session and OAuth client configuration", - "line_start": 3, - "line_end": 34, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/application.yml", - "accessed": "", - "claim_ids": [ - "AP2-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP3_TRADEOFF", - "title": "AP3와 AP1의 위협 모델·운영비 교환", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/bff-vs-spa-direct.md", - "heading": "BFF vs SPA direct", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md", - "accessed": "", - "claim_ids": [ - "AP3-C2" - ], - "decision_ids": [ - "AP3-D2" - ], - "priority": 100.0 - }, - { - "id": "AP3_PREFERENCE_SCOPE", - "title": "AP3 preferences 예시의 process-global state 간극", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "preferenceTheme and updatePreferences", - "line_start": 28, - "line_end": 95, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C8" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_ALTERNATIVE", - "title": "AP4 Traefik ForwardAuth 대안과 추가 비용", - "source_type": "branch-note", - "status": "config-tested", - "path": "docs/traefik-forwardauth-alternative.md", - "heading": "Traefik ForwardAuth alternative", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/traefik-forwardauth-alternative.md", - "accessed": "", - "claim_ids": [ - "AP4-C4" - ], - "decision_ids": [ - "AP4-D2" - ], - "priority": 85.0 - } - ], - "evidence_gap": false - }, - { - "section_id": "08-conclusion", - "intent": "conclusion", - "title": "결국 지키려던 것은 무엇이었나", - "reader_question": "세부 기술을 걷어냈을 때 남는 판단은 무엇인가?", - "decision_requirements": [], - "evidence": [ - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "AP1_BOUNDARY", - "title": "AP1 SPA direct의 OAuth·token 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/internal-spa-direct-no-google.md", - "heading": "AP1 internal SPA direct: local identity profile", - "line_start": 3, - "line_end": 10, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md", - "accessed": "", - "claim_ids": [ - "AP1-C1" - ], - "decision_ids": [ - "AP1-D1" - ], - "priority": 100.0 - }, - { - "id": "AP2_BOUNDARY", - "title": "AP2 confidential token mediator의 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap2-token-boundary.md", - "heading": "책임 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md", - "accessed": "", - "claim_ids": [ - "AP2-C1" - ], - "decision_ids": [ - "AP2-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_BOUNDARY", - "title": "AP3 BFF의 tokenless browser 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap3-bff-boundary.md", - "heading": "요청과 token 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md", - "accessed": "", - "claim_ids": [ - "AP3-C1" - ], - "decision_ids": [ - "AP3-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - } - ], - "evidence_gap": false - } - ], - "sources": [ - { - "id": "AP1_BOUNDARY", - "title": "AP1 SPA direct의 OAuth·token 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/internal-spa-direct-no-google.md", - "heading": "AP1 internal SPA direct: local identity profile", - "line_start": 3, - "line_end": 10, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md", - "accessed": "", - "claim_ids": [ - "AP1-C1" - ], - "decision_ids": [ - "AP1-D1" - ], - "priority": 100.0 - }, - { - "id": "AP1_STORAGE", - "title": "AP1 token 저장 선택과 수용 비용", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap1-token-storage.md", - "heading": "AP1 token storage trade-off", - "line_start": 3, - "line_end": 29, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md", - "accessed": "", - "claim_ids": [ - "AP1-C2" - ], - "decision_ids": [ - "AP1-D2" - ], - "priority": 100.0 - }, - { - "id": "AP1_LOGIN_RUNTIME", - "title": "AP1 SPA authorization, callback와 browser token data flow", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/src/app.js", - "heading": "UserManager configuration, callback, and renderSession", - "line_start": 9, - "line_end": 81, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js", - "accessed": "", - "claim_ids": [ - "AP1-C4" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP1_PKCE_DEMO_GAP", - "title": "AP1 manual PKCE helper와 실제 signin path의 구분", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "frontend/src/pkce.js", - "heading": "createPkcePair", - "line_start": 1, - "line_end": 25, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/pkce.js", - "accessed": "", - "claim_ids": [ - "AP1-C5" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP1_API_RUNTIME", - "title": "AP1 browser Bearer input에서 /api/me JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/src/app.js", - "heading": "callProtectedApi", - "line_start": 83, - "line_end": 109, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js", - "accessed": "", - "claim_ids": [ - "AP1-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP1_ROLE_FAILURE_RUNTIME", - "title": "AP1 JWT failure와 realm role authorization 경계", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "heading": "convert", - "line_start": 12, - "line_end": 27, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java", - "accessed": "", - "claim_ids": [ - "AP1-C7" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP1_GUARDRAILS", - "title": "AP1 public client와 Resource Server 가드레일", - "source_type": "canonical-project", - "status": "reviewed", - "path": "keycloak/import/keycloak-patterns-realm.json", - "heading": "spa-public client and realm token settings", - "line_start": 11, - "line_end": 73, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/keycloak/import/keycloak-patterns-realm.json", - "accessed": "", - "claim_ids": [ - "AP1-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP1_VERIFY", - "title": "AP1 브라우저 흐름과 token 수명주기 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern1.mjs", - "heading": "AP1 Playwright acceptance contract", - "line_start": 82, - "line_end": 202, - "url": "repo://keycloak-pattern/develop-keycloak-pattern1/e2e/pattern1.mjs", - "accessed": "", - "claim_ids": [ - "AP1-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP2_BOUNDARY", - "title": "AP2 confidential token mediator의 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap2-token-boundary.md", - "heading": "책임 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md", - "accessed": "", - "claim_ids": [ - "AP2-C1" - ], - "decision_ids": [ - "AP2-D1" - ], - "priority": 100.0 - }, - { - "id": "AP2_IMPLEMENTATION", - "title": "AP2 access-only handoff의 실제 구현", - "source_type": "canonical-project", - "status": "reviewed-discrepancy", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "heading": "accessToken", - "line_start": 28, - "line_end": 55, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "accessed": "", - "claim_ids": [ - "AP2-C2" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP2_LOGIN_FLOW", - "title": "AP2 browser entry와 Spring oauth2Login code 교환", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/resources/static/app.js", - "heading": "loginButton click and OAuth client registration", - "line_start": 7, - "line_end": 9, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/static/app.js", - "accessed": "", - "claim_ids": [ - "AP2-C4" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP2_BOUNDARY_RUNTIME", - "title": "AP2 token boundary endpoint input과 output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java", - "heading": "tokenBoundary", - "line_start": 25, - "line_end": 42, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java", - "accessed": "", - "claim_ids": [ - "AP2-C5" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP2_ACCESS_RUNTIME", - "title": "AP2 access handoff와 browser direct API의 data transformation", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "heading": "accessToken", - "line_start": 28, - "line_end": 54, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java", - "accessed": "", - "claim_ids": [ - "AP2-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP2_RESOURCE_RUNTIME", - "title": "AP2 Resource Server의 JWT input과 /api/me output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/ApiController.java", - "heading": "currentUser", - "line_start": 21, - "line_end": 28, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/backend/src/main/java/com/example/keycloakpattern/ApiController.java", - "accessed": "", - "claim_ids": [ - "AP2-C7" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP2_GUARDRAILS", - "title": "AP2 session, refresh custody, CORS와 audience 가드레일", - "source_type": "canonical-project", - "status": "reviewed", - "path": "token-mediator/src/main/resources/application.yml", - "heading": "AP2 session and OAuth client configuration", - "line_start": 3, - "line_end": 34, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/application.yml", - "accessed": "", - "claim_ids": [ - "AP2-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP2_VERIFY", - "title": "AP2 access-only 전달과 브라우저 직접 API 호출 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern2.mjs", - "heading": "AP2 Playwright acceptance contract", - "line_start": 39, - "line_end": 112, - "url": "repo://keycloak-pattern/develop-keycloak-pattern2/e2e/pattern2.mjs", - "accessed": "", - "claim_ids": [ - "AP2-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP3_BOUNDARY", - "title": "AP3 BFF의 tokenless browser 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap3-bff-boundary.md", - "heading": "요청과 token 경계", - "line_start": 3, - "line_end": 18, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md", - "accessed": "", - "claim_ids": [ - "AP3-C1" - ], - "decision_ids": [ - "AP3-D1" - ], - "priority": 100.0 - }, - { - "id": "AP3_TRADEOFF", - "title": "AP3와 AP1의 위협 모델·운영비 교환", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/bff-vs-spa-direct.md", - "heading": "BFF vs SPA direct", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md", - "accessed": "", - "claim_ids": [ - "AP3-C2" - ], - "decision_ids": [ - "AP3-D2" - ], - "priority": 100.0 - }, - { - "id": "AP3_GUARDRAILS", - "title": "AP3 CSRF token과 SameSite 가드레일", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "heading": "bffSecurity", - "line_start": 25, - "line_end": 58, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "accessed": "", - "claim_ids": [ - "AP3-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP3_LOGIN_FLOW", - "title": "AP3 oauth2Login과 server-side PKCE data flow", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "heading": "authorizationRequestResolver and bffSecurity", - "line_start": 20, - "line_end": 58, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java", - "accessed": "", - "claim_ids": [ - "AP3-C4" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP3_BOUNDARY_RUNTIME", - "title": "AP3 token boundary endpoint의 input과 관측 output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "tokenBoundary", - "line_start": 44, - "line_end": 64, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C5" - ], - "decision_ids": [], - "priority": 95.0 - }, - { - "id": "AP3_API_RUNTIME", - "title": "AP3 session input에서 downstream Bearer와 reader JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "currentUser and authorizedClient", - "line_start": 67, - "line_end": 111, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_CSRF_RUNTIME", - "title": "AP3 CSRF cookie-to-header transformation과 preferences output", - "source_type": "canonical-project", - "status": "reviewed", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "heading": "csrf", - "line_start": 14, - "line_end": 23, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java", - "accessed": "", - "claim_ids": [ - "AP3-C7" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP3_PREFERENCE_SCOPE", - "title": "AP3 preferences 예시의 process-global state 간극", - "source_type": "canonical-project", - "status": "reviewed-gap", - "path": "bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "heading": "preferenceTheme and updatePreferences", - "line_start": 28, - "line_end": 95, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java", - "accessed": "", - "claim_ids": [ - "AP3-C8" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP3_VERIFY", - "title": "AP3 browser token 비노출과 CSRF 방어 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern3.mjs", - "heading": "AP3 Playwright acceptance contract", - "line_start": 43, - "line_end": 191, - "url": "repo://keycloak-pattern/develop-keycloak-pattern3/e2e/pattern3.mjs", - "accessed": "", - "claim_ids": [ - "AP3-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP4_BOUNDARY", - "title": "AP4 oauth2-proxy와 nginx edge 책임 경계", - "source_type": "branch-note", - "status": "reviewed", - "path": "docs/ap4-edge-forward-auth.md", - "heading": "AP4 oauth2-proxy Edge Forward Auth", - "line_start": 3, - "line_end": 70, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md", - "accessed": "", - "claim_ids": [ - "AP4-C1" - ], - "decision_ids": [ - "AP4-D1" - ], - "priority": 100.0 - }, - { - "id": "AP4_NGINX", - "title": "AP4 auth_request와 identity header 덮어쓰기", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/default.conf.template", - "heading": "nginx AP4 server configuration", - "line_start": 13, - "line_end": 74, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/frontend/default.conf.template", - "accessed": "", - "claim_ids": [ - "AP4-C2" - ], - "decision_ids": [], - "priority": 90.0 - }, - { - "id": "AP4_LOGIN_RUNTIME", - "title": "AP4 unauthenticated navigation에서 oauth2-proxy session까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "docker-compose.yml", - "heading": "oauth2-proxy service", - "line_start": 92, - "line_end": 143, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docker-compose.yml", - "accessed": "", - "claim_ids": [ - "AP4-C5" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_REQUEST_RUNTIME", - "title": "AP4 external request에서 auth subrequest와 upstream input까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "frontend/default.conf.template", - "heading": "auth_request and upstream mapping", - "line_start": 13, - "line_end": 74, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/frontend/default.conf.template", - "accessed": "", - "claim_ids": [ - "AP4-C6" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_RESPONSE_RUNTIME", - "title": "AP4 trusted header input에서 edge identity JSON까지", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "heading": "currentUser and hasValidInternalToken", - "line_start": 27, - "line_end": 53, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "accessed": "", - "claim_ids": [ - "AP4-C7" - ], - "decision_ids": [], - "priority": 100.0 - }, - { - "id": "AP4_BACKEND", - "title": "AP4 upstream의 내부 token 검증", - "source_type": "canonical-project", - "status": "reviewed", - "path": "backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "heading": "currentUser and hasValidInternalToken", - "line_start": 18, - "line_end": 53, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java", - "accessed": "", - "claim_ids": [ - "AP4-C3" - ], - "decision_ids": [], - "priority": 85.0 - }, - { - "id": "AP4_VERIFY", - "title": "AP4 edge login과 header spoofing 방어 검증 계약", - "source_type": "canonical-project", - "status": "test-defined", - "path": "e2e/pattern4.mjs", - "heading": "AP4 Playwright acceptance contract", - "line_start": 44, - "line_end": 131, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/e2e/pattern4.mjs", - "accessed": "", - "claim_ids": [ - "AP4-T1" - ], - "decision_ids": [], - "priority": 80.0 - }, - { - "id": "AP4_ALTERNATIVE", - "title": "AP4 Traefik ForwardAuth 대안과 추가 비용", - "source_type": "branch-note", - "status": "config-tested", - "path": "docs/traefik-forwardauth-alternative.md", - "heading": "Traefik ForwardAuth alternative", - "line_start": 3, - "line_end": 22, - "url": "repo://keycloak-pattern/develop-keycloak-pattern4/docs/traefik-forwardauth-alternative.md", - "accessed": "", - "claim_ids": [ - "AP4-C4" - ], - "decision_ids": [ - "AP4-D2" - ], - "priority": 85.0 - }, - { - "id": "BRANCH_REACHABILITY", - "title": "네 pattern branch와 39개 feature ref의 도달성", - "source_type": "canonical-project", - "status": "partially-verified", - "path": "docs/keycloak-branch-manifest.tsv", - "heading": "branch target delivery registry", - "line_start": 1, - "line_end": 40, - "url": "repo://keycloak-pattern/develop/docs/keycloak-branch-manifest.tsv", - "accessed": "", - "claim_ids": [ - "COMMON-T1" - ], - "decision_ids": [], - "priority": 70.0 - }, - { - "id": "L4121b8d86b", - "title": "four pattern tradeoff matrix — Four Keycloak integration patterns", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "Four Keycloak integration patterns", - "line_start": 1, - "line_end": 14, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 45.74042 - }, - { - "id": "La5d0a70f24", - "title": "four pattern tradeoff matrix — 이 repository의 실행 증거", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "이 repository의 실행 증거", - "line_start": 28, - "line_end": 37, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 21.712857 - }, - { - "id": "L2c120c8093", - "title": "four pattern tradeoff matrix — 선택 기준", - "source_type": "local-document", - "status": "", - "path": "docs/four-pattern-tradeoff-matrix.md", - "heading": "선택 기준", - "line_start": 15, - "line_end": 27, - "url": "repo:///docs/four-pattern-tradeoff-matrix.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 17.280962 - }, - { - "id": "L4ec23ba045", - "title": "keycloak branch index — Keycloak branch implementation index", - "source_type": "local-document", - "status": "", - "path": "docs/keycloak-branch-index.md", - "heading": "Keycloak branch implementation index", - "line_start": 1, - "line_end": 29, - "url": "repo:///docs/keycloak-branch-index.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 14.830096 - }, - { - "id": "Lb39734ea9b", - "title": "google idp brokering — Google IdP brokering", - "source_type": "local-document", - "status": "", - "path": "docs/google-idp-brokering.md", - "heading": "Google IdP brokering", - "line_start": 1, - "line_end": 5, - "url": "repo:///docs/google-idp-brokering.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 5.851474 - }, - { - "id": "La28755902d", - "title": "google claim to role — Google claim-to-role mapping", - "source_type": "local-document", - "status": "", - "path": "docs/google-claim-to-role.md", - "heading": "Google claim-to-role mapping", - "line_start": 1, - "line_end": 18, - "url": "repo:///docs/google-claim-to-role.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 4.750257 - }, - { - "id": "Le8474e5ddd", - "title": "https termination — HTTPS termination: nginx or Caddy", - "source_type": "local-document", - "status": "", - "path": "docs/https-termination.md", - "heading": "HTTPS termination: nginx or Caddy", - "line_start": 1, - "line_end": 20, - "url": "repo:///docs/https-termination.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 2.684955 - }, - { - "id": "L0eb117abf5", - "title": "google redirect uri policy — Google redirect URI policy", - "source_type": "local-document", - "status": "", - "path": "docs/google-redirect-uri-policy.md", - "heading": "Google redirect URI policy", - "line_start": 1, - "line_end": 24, - "url": "repo:///docs/google-redirect-uri-policy.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 2.514945 - }, - { - "id": "L5d2c3b8016", - "title": "reverse proxy headers — Reverse proxy headers", - "source_type": "local-document", - "status": "", - "path": "docs/reverse-proxy-headers.md", - "heading": "Reverse proxy headers", - "line_start": 1, - "line_end": 15, - "url": "repo:///docs/reverse-proxy-headers.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 1.816984 - }, - { - "id": "L03b6abccb3", - "title": "google claim mapping — Google claim and identity mapping", - "source_type": "local-document", - "status": "", - "path": "docs/google-claim-mapping.md", - "heading": "Google claim and identity mapping", - "line_start": 1, - "line_end": 23, - "url": "repo:///docs/google-claim-mapping.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 1.503831 - }, - { - "id": "L0217277f31", - "title": "account linking sub vs email — Federated account key: `sub`, not email", - "source_type": "local-document", - "status": "", - "path": "docs/account-linking-sub-vs-email.md", - "heading": "Federated account key: `sub`, not email", - "line_start": 1, - "line_end": 18, - "url": "repo:///docs/account-linking-sub-vs-email.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 1.49767 - }, - { - "id": "L4a3b756b3d", - "title": "google idp brokering — Two verification profiles", - "source_type": "local-document", - "status": "", - "path": "docs/google-idp-brokering.md", - "heading": "Two verification profiles", - "line_start": 6, - "line_end": 28, - "url": "repo:///docs/google-idp-brokering.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 1.020519 - }, - { - "id": "Le9a41ffd86", - "title": "public domain tunneling — Public HTTPS domain for broker callbacks", - "source_type": "local-document", - "status": "", - "path": "docs/public-domain-tunneling.md", - "heading": "Public HTTPS domain for broker callbacks", - "line_start": 1, - "line_end": 21, - "url": "repo:///docs/public-domain-tunneling.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 0.729912 - }, - { - "id": "L55212df816", - "title": "first broker login security — First Broker Login security", - "source_type": "local-document", - "status": "", - "path": "docs/first-broker-login-security.md", - "heading": "First Broker Login security", - "line_start": 1, - "line_end": 27, - "url": "repo:///docs/first-broker-login-security.md", - "accessed": "", - "claim_ids": [], - "decision_ids": [], - "priority": 0.088108 - } - ] -} diff --git a/docs/keycloak/final/provenance.md b/docs/keycloak/final/provenance.md deleted file mode 100644 index a673b5f..0000000 --- a/docs/keycloak/final/provenance.md +++ /dev/null @@ -1,559 +0,0 @@ -# Evidence and decision provenance - -> This is an internal sidecar. It is not reader-facing article content. -> Source IDs, repository paths, line ranges, status labels, and access dates belong here—not in `document.md`. - -- Document: **브라우저 토큰에서 엣지 세션까지: Keycloak 인증 패턴 네 가지의 경계 설계** -- Citation rendering: `hidden` -- Evidence sources: **48** - -## Section evidence map - -| Section | Decision contract | Evidence | Status / location | -|---|---|---|---| -| 코드보다 먼저 드러난 문제 | — | `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns | `unspecified` · docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14) | -| ↳ | — | `AP1_BOUNDARY` AP1 SPA direct의 OAuth·token 책임 경계 | `reviewed` · docs/internal-spa-direct-no-google.md — AP1 internal SPA direct: local identity profile (lines 3-10) | -| ↳ | — | `AP2_BOUNDARY` AP2 confidential token mediator의 책임 경계 | `reviewed` · docs/ap2-token-boundary.md — 책임 경계 (lines 3-18) | -| ↳ | — | `AP3_BOUNDARY` AP3 BFF의 tokenless browser 경계 | `reviewed` · docs/ap3-bff-boundary.md — 요청과 token 경계 (lines 3-18) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | -| 문제를 어렵게 만든 제약 | — | `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns | `unspecified` · docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14) | -| ↳ | — | `AP1_STORAGE` AP1 token 저장 선택과 수용 비용 | `reviewed` · docs/ap1-token-storage.md — AP1 token storage trade-off (lines 3-29) | -| ↳ | — | `AP2_GUARDRAILS` AP2 session, refresh custody, CORS와 audience 가드레일 | `reviewed` · token-mediator/src/main/resources/application.yml — AP2 session and OAuth client configuration (lines 3-34) | -| ↳ | — | `AP3_TRADEOFF` AP3와 AP1의 위협 모델·운영비 교환 | `reviewed` · docs/bff-vs-spa-direct.md — BFF vs SPA direct (lines 3-22) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | -| ↳ | — | `AP4_RESPONSE_RUNTIME` AP4 trusted header input에서 edge identity JSON까지 | `reviewed` · backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java — currentUser and hasValidInternalToken (lines 27-53) | -| 검토한 선택지와 막힌 지점 | 상황·제약, 선택, 선택 이유, 검토한 대안, 수용한 비용, 보완 가드레일 | `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns | `unspecified` · docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14) | -| ↳ | — | `AP1_BOUNDARY` AP1 SPA direct의 OAuth·token 책임 경계 | `reviewed` · docs/internal-spa-direct-no-google.md — AP1 internal SPA direct: local identity profile (lines 3-10) | -| ↳ | — | `AP1_STORAGE` AP1 token 저장 선택과 수용 비용 | `reviewed` · docs/ap1-token-storage.md — AP1 token storage trade-off (lines 3-29) | -| ↳ | — | `AP2_BOUNDARY` AP2 confidential token mediator의 책임 경계 | `reviewed` · docs/ap2-token-boundary.md — 책임 경계 (lines 3-18) | -| ↳ | — | `AP2_IMPLEMENTATION` AP2 access-only handoff의 실제 구현 | `reviewed-discrepancy` · token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java — accessToken (lines 28-55) | -| ↳ | — | `AP3_BOUNDARY` AP3 BFF의 tokenless browser 경계 | `reviewed` · docs/ap3-bff-boundary.md — 요청과 token 경계 (lines 3-18) | -| ↳ | — | `AP3_TRADEOFF` AP3와 AP1의 위협 모델·운영비 교환 | `reviewed` · docs/bff-vs-spa-direct.md — BFF vs SPA direct (lines 3-22) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | -| ↳ | — | `AP4_ALTERNATIVE` AP4 Traefik ForwardAuth 대안과 추가 비용 | `config-tested` · docs/traefik-forwardauth-alternative.md — Traefik ForwardAuth alternative (lines 3-22) | -| 선택의 이유와 지킨 경계 | 상황·제약, 선택, 선택 이유, 검토한 대안, 수용한 비용, 보완 가드레일 | `AP1_BOUNDARY` AP1 SPA direct의 OAuth·token 책임 경계 | `reviewed` · docs/internal-spa-direct-no-google.md — AP1 internal SPA direct: local identity profile (lines 3-10) | -| ↳ | — | `AP1_STORAGE` AP1 token 저장 선택과 수용 비용 | `reviewed` · docs/ap1-token-storage.md — AP1 token storage trade-off (lines 3-29) | -| ↳ | — | `AP2_BOUNDARY` AP2 confidential token mediator의 책임 경계 | `reviewed` · docs/ap2-token-boundary.md — 책임 경계 (lines 3-18) | -| ↳ | — | `AP3_BOUNDARY` AP3 BFF의 tokenless browser 경계 | `reviewed` · docs/ap3-bff-boundary.md — 요청과 token 경계 (lines 3-18) | -| ↳ | — | `AP3_TRADEOFF` AP3와 AP1의 위협 모델·운영비 교환 | `reviewed` · docs/bff-vs-spa-direct.md — BFF vs SPA direct (lines 3-22) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | -| ↳ | — | `AP4_ALTERNATIVE` AP4 Traefik ForwardAuth 대안과 추가 비용 | `config-tested` · docs/traefik-forwardauth-alternative.md — Traefik ForwardAuth alternative (lines 3-22) | -| 선택이 코드와 흐름에 반영되는 방식 | — | `AP1_LOGIN_RUNTIME` AP1 SPA authorization, callback와 browser token data flow | `reviewed` · frontend/src/app.js — UserManager configuration, callback, and renderSession (lines 9-81) | -| ↳ | — | `AP1_PKCE_DEMO_GAP` AP1 manual PKCE helper와 실제 signin path의 구분 | `reviewed-gap` · frontend/src/pkce.js — createPkcePair (lines 1-25) | -| ↳ | — | `AP1_API_RUNTIME` AP1 browser Bearer input에서 /api/me JSON까지 | `reviewed` · frontend/src/app.js — callProtectedApi (lines 83-109) | -| ↳ | — | `AP1_ROLE_FAILURE_RUNTIME` AP1 JWT failure와 realm role authorization 경계 | `reviewed-gap` · backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java — convert (lines 12-27) | -| ↳ | — | `AP2_LOGIN_FLOW` AP2 browser entry와 Spring oauth2Login code 교환 | `reviewed` · token-mediator/src/main/resources/static/app.js — loginButton click and OAuth client registration (lines 7-9) | -| ↳ | — | `AP2_BOUNDARY_RUNTIME` AP2 token boundary endpoint input과 output | `reviewed` · token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java — tokenBoundary (lines 25-42) | -| ↳ | — | `AP2_ACCESS_RUNTIME` AP2 access handoff와 browser direct API의 data transformation | `reviewed` · token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java — accessToken (lines 28-54) | -| ↳ | — | `AP2_RESOURCE_RUNTIME` AP2 Resource Server의 JWT input과 /api/me output | `reviewed` · backend/src/main/java/com/example/keycloakpattern/ApiController.java — currentUser (lines 21-28) | -| ↳ | — | `AP3_LOGIN_FLOW` AP3 oauth2Login과 server-side PKCE data flow | `reviewed` · bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java — authorizationRequestResolver and bffSecurity (lines 20-58) | -| ↳ | — | `AP3_BOUNDARY_RUNTIME` AP3 token boundary endpoint의 input과 관측 output | `reviewed` · bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — tokenBoundary (lines 44-64) | -| ↳ | — | `AP3_API_RUNTIME` AP3 session input에서 downstream Bearer와 reader JSON까지 | `reviewed` · bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — currentUser and authorizedClient (lines 67-111) | -| ↳ | — | `AP3_CSRF_RUNTIME` AP3 CSRF cookie-to-header transformation과 preferences output | `reviewed` · bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java — csrf (lines 14-23) | -| ↳ | — | `AP3_PREFERENCE_SCOPE` AP3 preferences 예시의 process-global state 간극 | `reviewed-gap` · bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — preferenceTheme and updatePreferences (lines 28-95) | -| ↳ | — | `AP4_LOGIN_RUNTIME` AP4 unauthenticated navigation에서 oauth2-proxy session까지 | `reviewed` · docker-compose.yml — oauth2-proxy service (lines 92-143) | -| ↳ | — | `AP4_REQUEST_RUNTIME` AP4 external request에서 auth subrequest와 upstream input까지 | `reviewed` · frontend/default.conf.template — auth_request and upstream mapping (lines 13-74) | -| ↳ | — | `AP4_RESPONSE_RUNTIME` AP4 trusted header input에서 edge identity JSON까지 | `reviewed` · backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java — currentUser and hasValidInternalToken (lines 27-53) | -| 결정이 지켜지는지 확인하는 방법 | — | `AP1_VERIFY` AP1 브라우저 흐름과 token 수명주기 검증 계약 | `test-defined` · e2e/pattern1.mjs — AP1 Playwright acceptance contract (lines 82-202) | -| ↳ | — | `AP1_ROLE_FAILURE_RUNTIME` AP1 JWT failure와 realm role authorization 경계 | `reviewed-gap` · backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java — convert (lines 12-27) | -| ↳ | — | `AP2_VERIFY` AP2 access-only 전달과 브라우저 직접 API 호출 검증 계약 | `test-defined` · e2e/pattern2.mjs — AP2 Playwright acceptance contract (lines 39-112) | -| ↳ | — | `AP2_ACCESS_RUNTIME` AP2 access handoff와 browser direct API의 data transformation | `reviewed` · token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java — accessToken (lines 28-54) | -| ↳ | — | `AP3_VERIFY` AP3 browser token 비노출과 CSRF 방어 검증 계약 | `test-defined` · e2e/pattern3.mjs — AP3 Playwright acceptance contract (lines 43-191) | -| ↳ | — | `AP3_CSRF_RUNTIME` AP3 CSRF cookie-to-header transformation과 preferences output | `reviewed` · bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java — csrf (lines 14-23) | -| ↳ | — | `AP4_VERIFY` AP4 edge login과 header spoofing 방어 검증 계약 | `test-defined` · e2e/pattern4.mjs — AP4 Playwright acceptance contract (lines 44-131) | -| ↳ | — | `AP4_RESPONSE_RUNTIME` AP4 trusted header input에서 edge identity JSON까지 | `reviewed` · backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java — currentUser and hasValidInternalToken (lines 27-53) | -| ↳ | — | `BRANCH_REACHABILITY` 네 pattern branch와 39개 feature ref의 도달성 | `partially-verified` · docs/keycloak-branch-manifest.tsv — branch target delivery registry (lines 1-40) | -| 얻은 것, 잃은 것, 적용하지 않을 때 | 상황·제약, 선택, 선택 이유, 검토한 대안, 수용한 비용, 보완 가드레일 | `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns | `unspecified` · docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14) | -| ↳ | — | `AP1_STORAGE` AP1 token 저장 선택과 수용 비용 | `reviewed` · docs/ap1-token-storage.md — AP1 token storage trade-off (lines 3-29) | -| ↳ | — | `AP2_GUARDRAILS` AP2 session, refresh custody, CORS와 audience 가드레일 | `reviewed` · token-mediator/src/main/resources/application.yml — AP2 session and OAuth client configuration (lines 3-34) | -| ↳ | — | `AP3_TRADEOFF` AP3와 AP1의 위협 모델·운영비 교환 | `reviewed` · docs/bff-vs-spa-direct.md — BFF vs SPA direct (lines 3-22) | -| ↳ | — | `AP3_PREFERENCE_SCOPE` AP3 preferences 예시의 process-global state 간극 | `reviewed-gap` · bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — preferenceTheme and updatePreferences (lines 28-95) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | -| ↳ | — | `AP4_ALTERNATIVE` AP4 Traefik ForwardAuth 대안과 추가 비용 | `config-tested` · docs/traefik-forwardauth-alternative.md — Traefik ForwardAuth alternative (lines 3-22) | -| 결국 지키려던 것은 무엇이었나 | — | `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns | `unspecified` · docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14) | -| ↳ | — | `AP1_BOUNDARY` AP1 SPA direct의 OAuth·token 책임 경계 | `reviewed` · docs/internal-spa-direct-no-google.md — AP1 internal SPA direct: local identity profile (lines 3-10) | -| ↳ | — | `AP2_BOUNDARY` AP2 confidential token mediator의 책임 경계 | `reviewed` · docs/ap2-token-boundary.md — 책임 경계 (lines 3-18) | -| ↳ | — | `AP3_BOUNDARY` AP3 BFF의 tokenless browser 경계 | `reviewed` · docs/ap3-bff-boundary.md — 요청과 token 경계 (lines 3-18) | -| ↳ | — | `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 | `reviewed` · docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70) | - -## Source details - -### `AP1_BOUNDARY` AP1 SPA direct의 OAuth·token 책임 경계 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/internal-spa-direct-no-google.md — AP1 internal SPA direct: local identity profile (lines 3-10)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/docs/internal-spa-direct-no-google.md` -- Claim IDs: `AP1-C1` -- Decision IDs: `AP1-D1` -- Retrieval priority: `100.0000` - -### `AP1_STORAGE` AP1 token 저장 선택과 수용 비용 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/ap1-token-storage.md — AP1 token storage trade-off (lines 3-29)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/docs/ap1-token-storage.md` -- Claim IDs: `AP1-C2` -- Decision IDs: `AP1-D2` -- Retrieval priority: `100.0000` - -### `AP1_LOGIN_RUNTIME` AP1 SPA authorization, callback와 browser token data flow - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `frontend/src/app.js — UserManager configuration, callback, and renderSession (lines 9-81)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js` -- Claim IDs: `AP1-C4` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP1_PKCE_DEMO_GAP` AP1 manual PKCE helper와 실제 signin path의 구분 - -- Type: `canonical-project` -- Status: `reviewed-gap` -- Location: `frontend/src/pkce.js — createPkcePair (lines 1-25)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/pkce.js` -- Claim IDs: `AP1-C5` -- Decision IDs: — -- Retrieval priority: `85.0000` - -### `AP1_API_RUNTIME` AP1 browser Bearer input에서 /api/me JSON까지 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `frontend/src/app.js — callProtectedApi (lines 83-109)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/frontend/src/app.js` -- Claim IDs: `AP1-C6` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP1_ROLE_FAILURE_RUNTIME` AP1 JWT failure와 realm role authorization 경계 - -- Type: `canonical-project` -- Status: `reviewed-gap` -- Location: `backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java — convert (lines 12-27)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/backend/src/main/java/com/example/keycloakpattern/KeycloakRealmRoleConverter.java` -- Claim IDs: `AP1-C7` -- Decision IDs: — -- Retrieval priority: `90.0000` - -### `AP1_GUARDRAILS` AP1 public client와 Resource Server 가드레일 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `keycloak/import/keycloak-patterns-realm.json — spa-public client and realm token settings (lines 11-73)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/keycloak/import/keycloak-patterns-realm.json` -- Claim IDs: `AP1-C3` -- Decision IDs: — -- Retrieval priority: `85.0000` - -### `AP1_VERIFY` AP1 브라우저 흐름과 token 수명주기 검증 계약 - -- Type: `canonical-project` -- Status: `test-defined` -- Location: `e2e/pattern1.mjs — AP1 Playwright acceptance contract (lines 82-202)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern1/e2e/pattern1.mjs` -- Claim IDs: `AP1-T1` -- Decision IDs: — -- Retrieval priority: `80.0000` - -### `AP2_BOUNDARY` AP2 confidential token mediator의 책임 경계 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/ap2-token-boundary.md — 책임 경계 (lines 3-18)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/docs/ap2-token-boundary.md` -- Claim IDs: `AP2-C1` -- Decision IDs: `AP2-D1` -- Retrieval priority: `100.0000` - -### `AP2_IMPLEMENTATION` AP2 access-only handoff의 실제 구현 - -- Type: `canonical-project` -- Status: `reviewed-discrepancy` -- Location: `token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java — accessToken (lines 28-55)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java` -- Claim IDs: `AP2-C2` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP2_LOGIN_FLOW` AP2 browser entry와 Spring oauth2Login code 교환 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `token-mediator/src/main/resources/static/app.js — loginButton click and OAuth client registration (lines 7-9)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/static/app.js` -- Claim IDs: `AP2-C4` -- Decision IDs: — -- Retrieval priority: `90.0000` - -### `AP2_BOUNDARY_RUNTIME` AP2 token boundary endpoint input과 output - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java — tokenBoundary (lines 25-42)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/TokenBoundaryController.java` -- Claim IDs: `AP2-C5` -- Decision IDs: — -- Retrieval priority: `95.0000` - -### `AP2_ACCESS_RUNTIME` AP2 access handoff와 browser direct API의 data transformation - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java — accessToken (lines 28-54)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/java/com/example/keycloakpattern/mediator/AccessTokenController.java` -- Claim IDs: `AP2-C6` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP2_RESOURCE_RUNTIME` AP2 Resource Server의 JWT input과 /api/me output - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `backend/src/main/java/com/example/keycloakpattern/ApiController.java — currentUser (lines 21-28)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/backend/src/main/java/com/example/keycloakpattern/ApiController.java` -- Claim IDs: `AP2-C7` -- Decision IDs: — -- Retrieval priority: `95.0000` - -### `AP2_GUARDRAILS` AP2 session, refresh custody, CORS와 audience 가드레일 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `token-mediator/src/main/resources/application.yml — AP2 session and OAuth client configuration (lines 3-34)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/token-mediator/src/main/resources/application.yml` -- Claim IDs: `AP2-C3` -- Decision IDs: — -- Retrieval priority: `85.0000` - -### `AP2_VERIFY` AP2 access-only 전달과 브라우저 직접 API 호출 검증 계약 - -- Type: `canonical-project` -- Status: `test-defined` -- Location: `e2e/pattern2.mjs — AP2 Playwright acceptance contract (lines 39-112)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern2/e2e/pattern2.mjs` -- Claim IDs: `AP2-T1` -- Decision IDs: — -- Retrieval priority: `80.0000` - -### `AP3_BOUNDARY` AP3 BFF의 tokenless browser 경계 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/ap3-bff-boundary.md — 요청과 token 경계 (lines 3-18)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/docs/ap3-bff-boundary.md` -- Claim IDs: `AP3-C1` -- Decision IDs: `AP3-D1` -- Retrieval priority: `100.0000` - -### `AP3_TRADEOFF` AP3와 AP1의 위협 모델·운영비 교환 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/bff-vs-spa-direct.md — BFF vs SPA direct (lines 3-22)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/docs/bff-vs-spa-direct.md` -- Claim IDs: `AP3-C2` -- Decision IDs: `AP3-D2` -- Retrieval priority: `100.0000` - -### `AP3_GUARDRAILS` AP3 CSRF token과 SameSite 가드레일 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java — bffSecurity (lines 25-58)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java` -- Claim IDs: `AP3-C3` -- Decision IDs: — -- Retrieval priority: `85.0000` - -### `AP3_LOGIN_FLOW` AP3 oauth2Login과 server-side PKCE data flow - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java — authorizationRequestResolver and bffSecurity (lines 20-58)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/SecurityConfig.java` -- Claim IDs: `AP3-C4` -- Decision IDs: — -- Retrieval priority: `95.0000` - -### `AP3_BOUNDARY_RUNTIME` AP3 token boundary endpoint의 input과 관측 output - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — tokenBoundary (lines 44-64)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java` -- Claim IDs: `AP3-C5` -- Decision IDs: — -- Retrieval priority: `95.0000` - -### `AP3_API_RUNTIME` AP3 session input에서 downstream Bearer와 reader JSON까지 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — currentUser and authorizedClient (lines 67-111)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java` -- Claim IDs: `AP3-C6` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP3_CSRF_RUNTIME` AP3 CSRF cookie-to-header transformation과 preferences output - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java — csrf (lines 14-23)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/CsrfController.java` -- Claim IDs: `AP3-C7` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP3_PREFERENCE_SCOPE` AP3 preferences 예시의 process-global state 간극 - -- Type: `canonical-project` -- Status: `reviewed-gap` -- Location: `bff/src/main/java/com/example/keycloakpattern/bff/BffController.java — preferenceTheme and updatePreferences (lines 28-95)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/bff/src/main/java/com/example/keycloakpattern/bff/BffController.java` -- Claim IDs: `AP3-C8` -- Decision IDs: — -- Retrieval priority: `90.0000` - -### `AP3_VERIFY` AP3 browser token 비노출과 CSRF 방어 검증 계약 - -- Type: `canonical-project` -- Status: `test-defined` -- Location: `e2e/pattern3.mjs — AP3 Playwright acceptance contract (lines 43-191)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern3/e2e/pattern3.mjs` -- Claim IDs: `AP3-T1` -- Decision IDs: — -- Retrieval priority: `80.0000` - -### `AP4_BOUNDARY` AP4 oauth2-proxy와 nginx edge 책임 경계 - -- Type: `branch-note` -- Status: `reviewed` -- Location: `docs/ap4-edge-forward-auth.md — AP4 oauth2-proxy Edge Forward Auth (lines 3-70)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/docs/ap4-edge-forward-auth.md` -- Claim IDs: `AP4-C1` -- Decision IDs: `AP4-D1` -- Retrieval priority: `100.0000` - -### `AP4_NGINX` AP4 auth_request와 identity header 덮어쓰기 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `frontend/default.conf.template — nginx AP4 server configuration (lines 13-74)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/frontend/default.conf.template` -- Claim IDs: `AP4-C2` -- Decision IDs: — -- Retrieval priority: `90.0000` - -### `AP4_LOGIN_RUNTIME` AP4 unauthenticated navigation에서 oauth2-proxy session까지 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `docker-compose.yml — oauth2-proxy service (lines 92-143)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/docker-compose.yml` -- Claim IDs: `AP4-C5` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP4_REQUEST_RUNTIME` AP4 external request에서 auth subrequest와 upstream input까지 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `frontend/default.conf.template — auth_request and upstream mapping (lines 13-74)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/frontend/default.conf.template` -- Claim IDs: `AP4-C6` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP4_RESPONSE_RUNTIME` AP4 trusted header input에서 edge identity JSON까지 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java — currentUser and hasValidInternalToken (lines 27-53)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java` -- Claim IDs: `AP4-C7` -- Decision IDs: — -- Retrieval priority: `100.0000` - -### `AP4_BACKEND` AP4 upstream의 내부 token 검증 - -- Type: `canonical-project` -- Status: `reviewed` -- Location: `backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java — currentUser and hasValidInternalToken (lines 18-53)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/backend/src/main/java/com/example/keycloakpattern/EdgeIdentityController.java` -- Claim IDs: `AP4-C3` -- Decision IDs: — -- Retrieval priority: `85.0000` - -### `AP4_VERIFY` AP4 edge login과 header spoofing 방어 검증 계약 - -- Type: `canonical-project` -- Status: `test-defined` -- Location: `e2e/pattern4.mjs — AP4 Playwright acceptance contract (lines 44-131)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/e2e/pattern4.mjs` -- Claim IDs: `AP4-T1` -- Decision IDs: — -- Retrieval priority: `80.0000` - -### `AP4_ALTERNATIVE` AP4 Traefik ForwardAuth 대안과 추가 비용 - -- Type: `branch-note` -- Status: `config-tested` -- Location: `docs/traefik-forwardauth-alternative.md — Traefik ForwardAuth alternative (lines 3-22)` -- Public/reference URL: `repo://keycloak-pattern/develop-keycloak-pattern4/docs/traefik-forwardauth-alternative.md` -- Claim IDs: `AP4-C4` -- Decision IDs: `AP4-D2` -- Retrieval priority: `85.0000` - -### `BRANCH_REACHABILITY` 네 pattern branch와 39개 feature ref의 도달성 - -- Type: `canonical-project` -- Status: `partially-verified` -- Location: `docs/keycloak-branch-manifest.tsv — branch target delivery registry (lines 1-40)` -- Public/reference URL: `repo://keycloak-pattern/develop/docs/keycloak-branch-manifest.tsv` -- Claim IDs: `COMMON-T1` -- Decision IDs: — -- Retrieval priority: `70.0000` - -### `L4121b8d86b` four pattern tradeoff matrix — Four Keycloak integration patterns - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/four-pattern-tradeoff-matrix.md — Four Keycloak integration patterns (lines 1-14)` -- Public/reference URL: `repo:///docs/four-pattern-tradeoff-matrix.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `45.7404` - -### `La5d0a70f24` four pattern tradeoff matrix — 이 repository의 실행 증거 - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/four-pattern-tradeoff-matrix.md — 이 repository의 실행 증거 (lines 28-37)` -- Public/reference URL: `repo:///docs/four-pattern-tradeoff-matrix.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `21.7129` - -### `L2c120c8093` four pattern tradeoff matrix — 선택 기준 - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/four-pattern-tradeoff-matrix.md — 선택 기준 (lines 15-27)` -- Public/reference URL: `repo:///docs/four-pattern-tradeoff-matrix.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `17.2810` - -### `L4ec23ba045` keycloak branch index — Keycloak branch implementation index - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/keycloak-branch-index.md — Keycloak branch implementation index (lines 1-29)` -- Public/reference URL: `repo:///docs/keycloak-branch-index.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `14.8301` - -### `Lb39734ea9b` google idp brokering — Google IdP brokering - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/google-idp-brokering.md — Google IdP brokering (lines 1-5)` -- Public/reference URL: `repo:///docs/google-idp-brokering.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `5.8515` - -### `La28755902d` google claim to role — Google claim-to-role mapping - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/google-claim-to-role.md — Google claim-to-role mapping (lines 1-18)` -- Public/reference URL: `repo:///docs/google-claim-to-role.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `4.7503` - -### `Le8474e5ddd` https termination — HTTPS termination: nginx or Caddy - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/https-termination.md — HTTPS termination: nginx or Caddy (lines 1-20)` -- Public/reference URL: `repo:///docs/https-termination.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `2.6850` - -### `L0eb117abf5` google redirect uri policy — Google redirect URI policy - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/google-redirect-uri-policy.md — Google redirect URI policy (lines 1-24)` -- Public/reference URL: `repo:///docs/google-redirect-uri-policy.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `2.5149` - -### `L5d2c3b8016` reverse proxy headers — Reverse proxy headers - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/reverse-proxy-headers.md — Reverse proxy headers (lines 1-15)` -- Public/reference URL: `repo:///docs/reverse-proxy-headers.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `1.8170` - -### `L03b6abccb3` google claim mapping — Google claim and identity mapping - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/google-claim-mapping.md — Google claim and identity mapping (lines 1-23)` -- Public/reference URL: `repo:///docs/google-claim-mapping.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `1.5038` - -### `L0217277f31` account linking sub vs email — Federated account key: `sub`, not email - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/account-linking-sub-vs-email.md — Federated account key: `sub`, not email (lines 1-18)` -- Public/reference URL: `repo:///docs/account-linking-sub-vs-email.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `1.4977` - -### `L4a3b756b3d` google idp brokering — Two verification profiles - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/google-idp-brokering.md — Two verification profiles (lines 6-28)` -- Public/reference URL: `repo:///docs/google-idp-brokering.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `1.0205` - -### `Le9a41ffd86` public domain tunneling — Public HTTPS domain for broker callbacks - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/public-domain-tunneling.md — Public HTTPS domain for broker callbacks (lines 1-21)` -- Public/reference URL: `repo:///docs/public-domain-tunneling.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `0.7299` - -### `L55212df816` first broker login security — First Broker Login security - -- Type: `local-document` -- Status: `unspecified` -- Location: `docs/first-broker-login-security.md — First Broker Login security (lines 1-27)` -- Public/reference URL: `repo:///docs/first-broker-login-security.md` -- Claim IDs: — -- Decision IDs: — -- Retrieval priority: `0.0881` diff --git a/docs/keycloak/final/quality-report.md b/docs/keycloak/final/quality-report.md deleted file mode 100644 index ef521be..0000000 --- a/docs/keycloak/final/quality-report.md +++ /dev/null @@ -1,163 +0,0 @@ -# Quality report - -## Gate result - -**PASS** - -- Document type: `technical_blog` -- Reader language: `ko-KR` -- Citation mode: `hidden` -- Source pack: 48 entries -- Required H2 intents: 8/8, in contract order -- Deterministic lint: 100.0/100 -- ClariDoc word count: 9,043 -- Blockers: 0 -- Errors: 0 -- Warnings: 0 - -No mock-provider score is used as evidence of prose or factual quality. - -## Evidence collection - -All four pattern tips were inspected independently and read-only: - -| Pattern | Git tip | -|---|---| -| AP1 | `bb8fd9333d7da1c6424d6e0b039fc1c80c2cf0ca` | -| AP2 | `d019846f8725bdb0badde33043b020dc252e32ff` | -| AP3 | `934c5da5d6edc2429dfb558b773656e46f21d677` | -| AP4 | `f4aea65dc6255eae07b20ebbe21e02fb6115e563` | - -The canonical `develop` corpus was merged with branch-specific implementation, -configuration, test-contract, decision-rationale, and worked-flow evidence. The -expanded evidence covers browser entry, authorization callback, token/session -storage, controller and framework transformations, downstream requests, exact -reader JSON fields, failure statuses, and implementation gaps. - -The common comparison described AP2 as a short or one-time handoff, but the -current controller returns the raw Keycloak access token on repeatable -authenticated GET requests and has no nonce consumption or deletion. The -article therefore uses the implementation-backed term `access-only handoff`. - -## Reference-document calibration - -The requested comparison document was inspected for its authoring pattern. Its -important technique is a concrete initial HTTP input followed through actual -classes and methods, intermediate models, next-hop input, and observable final -output. The revised Keycloak article applies the same technique separately to: - -- AP1 login callback and browser Bearer `/api/me`; -- AP2 login session, `/token/boundary`, `/token/access`, and direct API call; -- AP3 login, `/bff/api/me`, CSRF issuance, and preference POST; -- AP4 login redirect, Nginx auth subrequest, upstream rewrite, and identity JSON. - -## Independent reviews and revisions - -### AP1 and AP2 technical review - -Initial result: **NEEDS REVISION** - -Material findings: - -- the general Authorization Code introduction implied that every pattern used a - `code_challenge`, contradicting AP2's unverified PKCE boundary; -- the AP1 browser E2E was said to assert `username`, while its live assertion is - UI status plus decoded audience; -- AP2's `AP2_SESSION` transaction role before callback was missing; -- CORS wording could be mistaken for network-level access control; -- dynamic audience examples looked exact; -- AP1's local wildcard redirect registration and the diagnostic-server negative - test shape were not explicit. - -All findings were revised. Targeted re-review result: **PASS**, with no remaining -blocker, error, or warning. - -### AP3 and AP4 technical review - -Initial result: **NEEDS REVISION** - -Material findings: - -- AP3 incorrectly placed the ID token in - `OAuth2AuthorizedClientService`; the corrected text separates access/refresh - token storage from the ID-token-derived OIDC principal in `SecurityContext`; -- AP4 was initially grouped with server-side session cookies and was assigned - persistent refresh-token custody; the corrected text describes the configured - minimal client-side cookie, absence of a shared server store, and the - unverified refresh lifecycle; -- the AP3 handler was named `updatePreferences` instead of the actual - `updatePreference`; -- the cross-site test was described beyond its exact assertion, which only - checks that `AP3_SESSION` is absent from the request. - -All findings were revised. Targeted re-review result: **PASS**, with no adjacent -regression found. - -### Reader, logic, evidence, and procedure review - -Initial result: **PASS WITH ONE WARNING** - -The only warning was that “oauth2-proxy manages the Keycloak session” could -confuse the IdP SSO session with AP4's proxy session. The sentence now says that -oauth2-proxy owns OIDC code exchange and `AP4_SESSION` verification while Nginx -owns upstream request and identity-header construction. - -The reviewer confirmed that: - -- all four patterns contain a complete - `input → transformation → next hop → output` trace; -- test-defined contracts remain separate from fresh execution claims; -- hidden source metadata does not leak into reader prose; -- destructive volume reset is surrounded by prerequisites, stop conditions, - backup, and recovery guidance; -- each choice includes context, rationale, alternative, accepted cost, - guardrail, and evidence boundary. - -## Validation performed - -- Brief and 48-entry SourcePack runtime validation: **PASS** -- Deterministic preliminary outline generation: **PASS** -- Refined outline contract: **PASS**, 8 required intents in order -- Every refined-outline evidence ID resolves in the SourcePack: **PASS** -- Reader/provenance separation and hidden-citation leakage lint: **PASS** -- Final deterministic document lint: **PASS**, 100.0/100, 0 issues -- ClariDoc unit and integration suite: **PASS**, 41 tests -- Target repository after inspection: clean `develop` -- Four remote pattern refs re-resolved to the reviewed Git tips: **PASS** -- Earlier read-only manifest/ref audit: 39 declared feature refs exist and are - reachable from their declared target branches - -## Validation deliberately not performed - -The pattern verification scripts rebuild their stacks after deleting Docker -volumes. They were not run during this documentation task. The article therefore -describes committed acceptance contracts rather than claiming a fresh runtime -PASS. - -The repository-provided branch audit still depends on a branch-note inventory -outside the available local path. The separate read-only Git reachability check -does not substitute for those missing note files. - -The ClariDoc `scripts/verify.sh` workflow was not run because this worktree -already contains extensive user changes and that workflow removes or rebuilds -distribution and generated demo artifacts. The non-destructive 41-test suite, -contract validation, outline validation, and final lint were run instead. - -## Remaining evidence boundaries - -- AP1's E2E does not directly assert the token-request `code_verifier`, automatic - silent renewal, exact IdP cookie flags, CORS preflight, invalid signature, or - expired-token negative cases. Its local realm uses wildcard redirects. -- AP2 does not implement one-time consumption and does not verify expiry-driven - refresh, logout, durable session/token storage, or handoff replay rejection. -- AP3 does not implement a shared session/token store, token-at-rest encryption, - coordinated logout, downstream failure mapping, or user-scoped persistence - for the preference example. -- AP4's minimal cookie does not establish persistent refresh-token custody. Role - propagation, generalized endpoint enforcement, state-changing-request CSRF, - multi-replica cookie-secret lifecycle, and internal secret rotation remain - outside the verified contract. -- The Google harness uses a controllable mock identity provider. Real external - accounts and public-HTTPS acceptance remain separate. -- Performance, load, high availability, recovery, and long-duration session - behavior are outside the verified scope.