131 lines
11 KiB
XML
131 lines
11 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="1235" height="539" viewBox="0 0 1235 539" role="img" aria-labelledby="diagram-title diagram-description">
|
|
<title id="diagram-title">로그인 credential owner와 API caller는 같은 축이 아니다</title>
|
|
<desc id="diagram-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 조립을 맡는다.</desc>
|
|
<metadata>{"techviz":{"spec_version":"1.1","id":"login-api-phase-split","profile":"component-flow"},"source_context":{"document":"document.md","document_sha256":"df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371","anchor":{"kind":"marker","value":"login-api-phase-split","line":42}},"evidence_policy":"Each factual element cites source lines or is marked assumption.","diagram_only":true}</metadata>
|
|
<defs>
|
|
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
<path d="M 0 0 L 10 5 L 0 10 z" />
|
|
</marker>
|
|
<style>
|
|
:root { color-scheme: light; }
|
|
text { font-family: Inter, Pretendard, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #111827; }
|
|
.canvas { fill: #ffffff; }
|
|
.group-box { fill: #ffffff; stroke: #9ca3af; stroke-width: 1.4; stroke-dasharray: 7 5; }
|
|
.group-label-bg { fill: #ffffff; }
|
|
.group-label { font-size: 13px; font-weight: 650; fill: #374151; }
|
|
.edge { fill: none; stroke: #374151; stroke-width: 1.8; stroke-linejoin: round; stroke-linecap: round; marker-end: url(#arrow); }
|
|
.edge.style-dashed, .edge.semantic-dashed, .edge.assumption { stroke-dasharray: 7 5; }
|
|
.edge.style-dotted { stroke-dasharray: 2 5; }
|
|
.edge.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
|
.edge.emphasis-muted { stroke: #9ca3af; }
|
|
.edge.emphasis-warning, .edge.kind-failure, .edge.kind-error { stroke: #dc2626; stroke-width: 2.2; }
|
|
.edge-label-bg { fill: #ffffff; }
|
|
.edge-label { font-size: 12px; font-weight: 560; text-anchor: middle; }
|
|
.node-shape { fill: #ffffff; stroke: #4b5563; stroke-width: 1.7; }
|
|
.node-shape.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
|
.node-shape.emphasis-muted { stroke: #9ca3af; fill: #f9fafb; }
|
|
.node-shape.emphasis-warning { stroke: #d97706; stroke-width: 2; fill: #fffdf5; }
|
|
.node-shape.kind-database, .node-shape.kind-datastore, .node-shape.kind-storage { fill: #f8fafc; }
|
|
.node-shape.kind-queue, .node-shape.kind-event, .node-shape.kind-topic { fill: #fafafa; }
|
|
.node-shape.assumption { stroke-dasharray: 4 4; }
|
|
.storage-bottom, .controller-divider { fill: none; stroke: #4b5563; stroke-width: 1.4; }
|
|
.controller-led { fill: #4b5563; }
|
|
.actor-symbol { fill: none; stroke: #4b5563; stroke-width: 1.8; stroke-linecap: round; }
|
|
.actor-symbol.emphasis-primary { stroke: #2563eb; stroke-width: 2.2; }
|
|
.node-label { font-size: 14px; font-weight: 650; text-anchor: middle; }
|
|
.node-role { font-size: 10px; letter-spacing: 0.04em; text-anchor: middle; fill: #6b7280; }
|
|
.node-detail-divider { stroke: #d1d5db; stroke-width: 1; }
|
|
.node-detail { font-size: 11px; fill: #374151; }
|
|
.assumption-badge { font-size: 9px; font-weight: 700; fill: #92400e; }
|
|
.failure-mark { stroke: #dc2626; stroke-width: 4; stroke-linecap: round; }
|
|
.lifeline { stroke: #9ca3af; stroke-width: 1.2; stroke-dasharray: 5 5; }
|
|
.timeline-axis { stroke: #374151; stroke-width: 1.8; marker-end: url(#arrow); }
|
|
.timeline-stem { stroke: #6b7280; stroke-width: 1.3; }
|
|
.timeline-marker { fill: #ffffff; stroke: #374151; stroke-width: 1.7; }
|
|
.timeline-marker.primary { fill: #2563eb; stroke: #2563eb; }
|
|
.timeline-marker.warning { fill: #dc2626; stroke: #dc2626; }
|
|
.timeline-label { font-size: 13px; font-weight: 650; text-anchor: middle; }
|
|
.timeline-detail { font-size: 11px; fill: #4b5563; text-anchor: middle; }
|
|
</style>
|
|
</defs>
|
|
<rect class="canvas" width="1235" height="539" />
|
|
<rect class="group-box" x="40.0" y="41.5" width="520.0" height="446.0" rx="8" />
|
|
<rect class="group-label-bg" x="54.0" y="31.5" width="90.0" height="22" />
|
|
<text class="group-label" x="64.0" y="46.5">로그인 구간</text>
|
|
<rect class="group-box" x="660.0" y="35.0" width="530.0" height="459.0" rx="8" />
|
|
<rect class="group-label-bg" x="674.0" y="25.0" width="106.0" height="22" />
|
|
<text class="group-label" x="684.0" y="40.0">애플리케이션 요청 구간</text>
|
|
<polyline class="edge kind-request style-solid emphasis-normal" points="845.0,123.0 927.5,123.0 927.5,256.5 1010.0,256.5" data-evidence="35-40" />
|
|
<rect class="edge-label-bg" x="888.9" y="175.8" width="125.2" height="22" rx="3" />
|
|
<text class="edge-label" x="951.5" y="190.8">/api/me · Bearer</text>
|
|
<polyline class="edge kind-handoff style-solid emphasis-normal" points="530.0,123.0 612.5,123.0 612.5,123.0 695.0,123.0" data-evidence="35-35" />
|
|
<rect class="edge-label-bg" x="576.7" y="81.0" width="71.6" height="22" rx="3" />
|
|
<text class="edge-label" x="612.5" y="96.0">owner 분리</text>
|
|
<polyline class="edge kind-request style-solid emphasis-normal" points="845.0,272.5 927.5,272.5 927.5,274.5 1010.0,274.5" data-evidence="35-40" />
|
|
<rect class="edge-label-bg" x="888.9" y="259.5" width="125.2" height="22" rx="3" />
|
|
<text class="edge-label" x="951.5" y="274.5">/api/me · Bearer</text>
|
|
<polyline class="edge kind-responsibility style-solid emphasis-normal" points="530.0,266.0 612.5,266.0 612.5,272.5 695.0,272.5" data-evidence="35-35" />
|
|
<rect class="edge-label-bg" x="607.4" y="255.2" width="58.2" height="22" rx="3" />
|
|
<text class="edge-label" x="636.5" y="270.2">동일 BFF</text>
|
|
<polyline class="edge kind-handoff style-solid emphasis-normal" points="530.0,417.5 610.0,417.5 610.0,424.0 690.0,424.0" data-evidence="35-35" />
|
|
<rect class="edge-label-bg" x="601.5" y="406.8" width="64.9" height="22" rx="3" />
|
|
<text class="edge-label" x="634.0" y="421.8">auth 결과</text>
|
|
<polyline class="edge kind-request style-solid emphasis-normal" points="850.0,424.0 930.0,424.0 930.0,292.5 1010.0,292.5" data-evidence="35-40" />
|
|
<rect class="edge-label-bg" x="881.4" y="344.2" width="145.3" height="22" rx="3" />
|
|
<text class="edge-label" x="954.0" y="359.2">/edge/me · identity</text>
|
|
<polyline class="edge kind-data style-solid emphasis-normal" points="220.0,256.5 300.0,256.5 300.0,123.0 380.0,123.0" data-evidence="33-35" />
|
|
<rect class="edge-label-bg" x="274.8" y="175.8" width="98.4" height="22" rx="3" />
|
|
<text class="edge-label" x="324.0" y="190.8">code → token</text>
|
|
<polyline class="edge kind-data style-solid emphasis-normal" points="220.0,274.5 300.0,274.5 300.0,266.0 380.0,266.0" data-evidence="33-35" />
|
|
<rect class="edge-label-bg" x="274.8" y="256.2" width="98.4" height="22" rx="3" />
|
|
<text class="edge-label" x="324.0" y="271.2">code → token</text>
|
|
<polyline class="edge kind-data style-solid emphasis-normal" points="220.0,292.5 300.0,292.5 300.0,417.5 380.0,417.5" data-evidence="33-35" />
|
|
<rect class="edge-label-bg" x="274.8" y="341.0" width="98.4" height="22" rx="3" />
|
|
<text class="edge-label" x="324.0" y="356.0">OIDC code 교환</text>
|
|
<g id="node-keycloak">
|
|
<rect class="node-shape kind-service emphasis-normal role-source" data-evidence="33-33" x="70.0" y="242.5" width="150.0" height="64.0" rx="7" />
|
|
<text class="node-label" x="145.0" y="272.5">Keycloak</text>
|
|
</g>
|
|
<g id="node-ap2-mediator-login">
|
|
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="35-35" x="380.0" y="87.5" width="150.0" height="71.0" rx="7" />
|
|
<text class="node-label" x="455.0" y="114.5">AP2 mediator</text>
|
|
<line class="node-detail-divider" x1="394.0" y1="135.5" x2="516.0" y2="135.5" />
|
|
<text class="node-detail" x="396.0" y="152.5">token 발급 주체</text>
|
|
</g>
|
|
<g id="node-ap3-bff-login">
|
|
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="35-35" x="380.0" y="230.5" width="150.0" height="71.0" rx="7" />
|
|
<text class="node-label" x="455.0" y="257.5">AP3 BFF · 로그인</text>
|
|
<line class="node-detail-divider" x1="394.0" y1="278.5" x2="516.0" y2="278.5" />
|
|
<text class="node-detail" x="396.0" y="295.5">token 발급 주체</text>
|
|
</g>
|
|
<g id="node-ap4-oauth2-proxy">
|
|
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="35-35" x="380.0" y="373.5" width="150.0" height="88.0" rx="7" />
|
|
<text class="node-label" x="455.0" y="400.5">AP4 oauth2-proxy</text>
|
|
<line class="node-detail-divider" x1="394.0" y1="421.5" x2="516.0" y2="421.5" />
|
|
<text class="node-detail" x="396.0" y="438.5">OIDC code 교환</text>
|
|
<text class="node-detail" x="396.0" y="454.5">AP4_SESSION 검증</text>
|
|
</g>
|
|
<g id="node-ap2-browser-caller">
|
|
<g class="actor-symbol emphasis-normal" data-evidence="35-35"><circle cx="770.0" cy="101.0" r="11.0" /><line x1="770.0" y1="117.0" x2="770.0" y2="136.0" /><line x1="752.0" y1="127.0" x2="788.0" y2="127.0" /><line x1="770.0" y1="136.0" x2="755.0" y2="153.0" /><line x1="770.0" y1="136.0" x2="785.0" y2="153.0" /></g>
|
|
<text class="node-label" x="770.0" y="158.0">AP2 브라우저</text>
|
|
</g>
|
|
<g id="node-ap3-bff-caller">
|
|
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="35-35" x="695.0" y="237.0" width="150.0" height="71.0" rx="7" />
|
|
<text class="node-label" x="770.0" y="264.0">AP3 BFF · API</text>
|
|
<line class="node-detail-divider" x1="709.0" y1="285.0" x2="831.0" y2="285.0" />
|
|
<text class="node-detail" x="711.0" y="302.0">API 호출 주체</text>
|
|
</g>
|
|
<g id="node-ap4-nginx-caller">
|
|
<rect class="node-shape kind-service emphasis-normal role-service" data-evidence="35-35" x="690.0" y="380.0" width="160.0" height="88.0" rx="7" />
|
|
<text class="node-label" x="770.0" y="407.0">AP4 Nginx</text>
|
|
<line class="node-detail-divider" x1="704.0" y1="428.0" x2="836.0" y2="428.0" />
|
|
<text class="node-detail" x="706.0" y="445.0">upstream 요청 연결</text>
|
|
<text class="node-detail" x="706.0" y="461.0">identity header 조립</text>
|
|
</g>
|
|
<g id="node-protected-resource">
|
|
<rect class="node-shape kind-service emphasis-primary role-sink" data-evidence="40-40" x="1010.0" y="242.5" width="150.0" height="64.0" rx="7" />
|
|
<text class="node-label" x="1085.0" y="272.5">보호 자원</text>
|
|
</g>
|
|
</svg>
|