368 lines
9.7 KiB
JSON
368 lines
9.7 KiB
JSON
{
|
|
"version": "1.1",
|
|
"id": "login-api-phase-split",
|
|
"title": "로그인 credential owner와 API caller는 같은 축이 아니다",
|
|
"question": "로그인 구간의 credential owner와 애플리케이션 요청 구간의 caller는 AP2·AP3·AP4에서 어디서 같고 갈라지는가?",
|
|
"type": "architecture",
|
|
"direction": "LR",
|
|
"audience": [
|
|
"인증 아키텍처를 비교하는 개발자",
|
|
"보안 설계 검토자"
|
|
],
|
|
"summary": "AP2는 mediator와 브라우저로 책임이 갈리고, AP3는 BFF가 두 책임을 함께 가지며, AP4는 oauth2-proxy의 인증 결과를 Nginx가 upstream 요청으로 바꾼다.",
|
|
"alt": "로그인 구간과 애플리케이션 요청 구간을 나누어 AP2 mediator·브라우저, AP3 BFF, AP4 oauth2-proxy·Nginx의 책임 배치를 비교한 다이어그램.",
|
|
"long_description": "왼쪽 로그인 구간에는 Keycloak과 AP2 mediator, AP3 BFF, AP4 oauth2-proxy가 있다. Keycloak의 authorization code는 각 OAuth client 쪽으로 이동한다. 오른쪽 애플리케이션 요청 구간에는 AP2 브라우저, AP3 BFF, AP4 Nginx와 보호 자원이 있다. AP2는 token을 받는 mediator와 API를 호출하는 브라우저가 갈리고, AP3는 같은 BFF가 token 발급과 API 호출을 소유한다. AP4는 oauth2-proxy가 code 교환과 session 검증을 맡고 Nginx가 upstream 요청 연결과 identity header 조립을 맡는다.",
|
|
"source_context": {
|
|
"document": "document.md",
|
|
"document_sha256": "e2cc061f683294c937f089ad874cbf7a617f7313c07a92c2148fb11287a0da37",
|
|
"anchor": {
|
|
"kind": "heading",
|
|
"value": "로그인 흐름과 API 흐름은 같은 선이 아니다",
|
|
"line": 31
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "component-flow",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-event-flow"
|
|
],
|
|
"rationale": "원문은 Keycloak에서 세 로그인 owner로 갈라지고 다시 세 API caller를 거쳐 보호 자원으로 합쳐지는 책임 흐름을 명시한다. 두 lifecycle group을 유지한 connected fan-out/join component-flow가 owner 이동을 가장 직접적으로 보여 준다.",
|
|
"focus_node": "protected-resource"
|
|
},
|
|
"groups": [
|
|
{
|
|
"id": "login-phase",
|
|
"label": "로그인 구간",
|
|
"kind": "lifecycle",
|
|
"role": "zone",
|
|
"description": "Authorization request, callback, code 교환과 로그인 상태 생성이 일어나는 구간.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 39,
|
|
"end_line": 41
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "application-request-phase",
|
|
"label": "애플리케이션 요청 구간",
|
|
"kind": "lifecycle",
|
|
"role": "zone",
|
|
"description": "브라우저 입력부터 credential 변환, 보호 자원 검증과 최종 응답까지의 구간.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 39,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"nodes": [
|
|
{
|
|
"id": "keycloak",
|
|
"label": "Keycloak",
|
|
"kind": "service",
|
|
"group": "login-phase",
|
|
"role": "source",
|
|
"description": "Authorization request를 받고 authorization code를 OAuth client에 돌려준다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 33,
|
|
"end_line": 33
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap2-mediator-login",
|
|
"label": "AP2 mediator",
|
|
"kind": "service",
|
|
"group": "login-phase",
|
|
"role": "service",
|
|
"details": [
|
|
"token 발급 주체"
|
|
],
|
|
"description": "AP2에서 token을 발급받는 주체.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap3-bff-login",
|
|
"label": "AP3 BFF · 로그인",
|
|
"kind": "service",
|
|
"group": "login-phase",
|
|
"role": "service",
|
|
"details": [
|
|
"token 발급 주체"
|
|
],
|
|
"description": "AP3에서 token을 발급받는 BFF의 로그인 책임.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap4-oauth2-proxy",
|
|
"label": "AP4 oauth2-proxy",
|
|
"kind": "service",
|
|
"group": "login-phase",
|
|
"role": "service",
|
|
"details": [
|
|
"OIDC code 교환",
|
|
"AP4_SESSION 검증"
|
|
],
|
|
"description": "AP4에서 OIDC code 교환과 proxy session 검증을 맡는다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap2-browser-caller",
|
|
"label": "AP2 브라우저",
|
|
"kind": "actor",
|
|
"group": "application-request-phase",
|
|
"role": "actor",
|
|
"shape": "actor",
|
|
"details": [
|
|
"API 호출 주체"
|
|
],
|
|
"description": "AP2에서 token 발급 주체와 달리 실제 API를 호출한다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap3-bff-caller",
|
|
"label": "AP3 BFF · API",
|
|
"kind": "service",
|
|
"group": "application-request-phase",
|
|
"role": "service",
|
|
"details": [
|
|
"API 호출 주체"
|
|
],
|
|
"description": "AP3에서 token을 발급받은 같은 BFF가 API도 호출한다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap4-nginx-caller",
|
|
"label": "AP4 Nginx",
|
|
"kind": "service",
|
|
"group": "application-request-phase",
|
|
"role": "service",
|
|
"details": [
|
|
"upstream 요청 연결",
|
|
"identity header 조립"
|
|
],
|
|
"description": "AP4에서 upstream 요청을 연결하고 identity header를 조립한다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "protected-resource",
|
|
"label": "보호 자원",
|
|
"kind": "service",
|
|
"group": "application-request-phase",
|
|
"role": "sink",
|
|
"emphasis": "primary",
|
|
"description": "애플리케이션 요청 구간에서 전달된 credential을 검증하고 최종 응답을 만든다.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 42,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false,
|
|
"details": [
|
|
"/api/me",
|
|
"/edge/me"
|
|
]
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "keycloak-to-ap2",
|
|
"from": "keycloak",
|
|
"to": "ap2-mediator-login",
|
|
"label": "code → token",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 33,
|
|
"end_line": 33
|
|
},
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "keycloak-to-ap3",
|
|
"from": "keycloak",
|
|
"to": "ap3-bff-login",
|
|
"label": "code → token",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 33,
|
|
"end_line": 33
|
|
},
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "keycloak-to-ap4",
|
|
"from": "keycloak",
|
|
"to": "ap4-oauth2-proxy",
|
|
"label": "OIDC code 교환",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 33,
|
|
"end_line": 33
|
|
},
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap2-owner-split",
|
|
"from": "ap2-mediator-login",
|
|
"to": "ap2-browser-caller",
|
|
"label": "owner 분리",
|
|
"kind": "handoff",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap3-same-owner",
|
|
"from": "ap3-bff-login",
|
|
"to": "ap3-bff-caller",
|
|
"label": "동일 BFF",
|
|
"kind": "responsibility",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap4-owner-handoff",
|
|
"from": "ap4-oauth2-proxy",
|
|
"to": "ap4-nginx-caller",
|
|
"label": "auth 결과",
|
|
"kind": "handoff",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap2-api-request",
|
|
"from": "ap2-browser-caller",
|
|
"to": "protected-resource",
|
|
"label": "Bearer",
|
|
"kind": "request",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
},
|
|
{
|
|
"start_line": 41,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap3-api-request",
|
|
"from": "ap3-bff-caller",
|
|
"to": "protected-resource",
|
|
"label": "Bearer",
|
|
"kind": "request",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
},
|
|
{
|
|
"start_line": 41,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "ap4-upstream-request",
|
|
"from": "ap4-nginx-caller",
|
|
"to": "protected-resource",
|
|
"label": "identity header",
|
|
"kind": "request",
|
|
"evidence": [
|
|
{
|
|
"start_line": 37,
|
|
"end_line": 37
|
|
},
|
|
{
|
|
"start_line": 41,
|
|
"end_line": 42
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "시간 순서를 반복하는 sequence가 아니라 두 lifecycle group을 가로지르는 owner fan-out과 API caller join 관계만 표현했다."
|
|
}
|
|
}
|