Files
document-haness/.run/keycloak-four-patterns/final/evidence-map.json
T

2050 lines
69 KiB
JSON

{
"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
}
]
}