Files
technical-visualization-haness/.work/keycloak-four-patterns/final/assets/ap1-browser-bearer-flow/ap1-browser-bearer-flow.svg
T
2026-07-29 18:03:21 +09:00

81 lines
7.1 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="680" height="542" viewBox="0 0 680 542" role="img" aria-labelledby="diagram-title diagram-description">
<title id="diagram-title">AP1 callback code에서 브라우저 Bearer 요청까지</title>
<desc id="diagram-description">브라우저 SPA가 S256 code challenge가 포함된 authorization request를 Keycloak에 보낸다. Keycloak이 code와 state를 callback으로 돌려주면 SPA는 원래 verifier를 포함해 token endpoint에 code를 제출하고 access, refresh, ID token을 받는다. 이어서 SPA가 access token을 Authorization Bearer header에 넣어 Resource Server의 /api/me를 직접 호출하고 사용자 JSON을 받는다.</desc>
<metadata>{&quot;techviz&quot;:{&quot;spec_version&quot;:&quot;1.1&quot;,&quot;id&quot;:&quot;ap1-browser-bearer-flow&quot;,&quot;profile&quot;:&quot;sequence&quot;},&quot;source_context&quot;:{&quot;document&quot;:&quot;document.md&quot;,&quot;document_sha256&quot;:&quot;df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371&quot;,&quot;anchor&quot;:{&quot;kind&quot;:&quot;marker&quot;,&quot;value&quot;:&quot;ap1-browser-bearer-flow&quot;,&quot;line&quot;:395}},&quot;evidence_policy&quot;:&quot;Each factual element cites source lines or is marked assumption.&quot;,&quot;diagram_only&quot;: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="680" height="542" />
<rect class="node-shape kind-participant emphasis-primary role-participant" data-evidence="201-236,240-280,303-322" x="45.0" y="35.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="120.0" y="65.0">브라우저 SPA</text>
<line class="lifeline" x1="120.0" y1="99.0" x2="120.0" y2="512.0" />
<rect class="node-shape kind-participant emphasis-normal role-participant" data-evidence="223-263" x="255.0" y="35.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="330.0" y="65.0">Keycloak</text>
<line class="lifeline" x1="330.0" y1="99.0" x2="330.0" y2="512.0" />
<rect class="node-shape kind-participant emphasis-normal role-participant" data-evidence="311-353" x="465.0" y="35.0" width="150.0" height="64.0" rx="7" />
<text class="node-label" x="540.0" y="65.0">Resource Server</text>
<line class="lifeline" x1="540.0" y1="99.0" x2="540.0" y2="512.0" />
<polyline class="edge kind-request style-solid emphasis-normal" points="120.0,140.0 330.0,140.0" data-evidence="223-236" />
<rect class="edge-label-bg" x="78.7" y="114.0" width="292.7" height="22" rx="3" />
<text class="edge-label" x="225.0" y="129.0">1. authorization request · S256 challenge</text>
<polyline class="edge kind-response style-dashed emphasis-normal semantic-dashed" points="330.0,202.0 120.0,202.0" data-evidence="240-250" />
<rect class="edge-label-bg" x="128.9" y="176.0" width="192.2" height="22" rx="3" />
<text class="edge-label" x="225.0" y="191.0">2. callback · code + state</text>
<polyline class="edge kind-request style-solid emphasis-normal" points="120.0,264.0 330.0,264.0" data-evidence="250-261" />
<rect class="edge-label-bg" x="102.1" y="238.0" width="245.8" height="22" rx="3" />
<text class="edge-label" x="225.0" y="253.0">3. token request · code + verifier</text>
<polyline class="edge kind-response style-dashed emphasis-normal semantic-dashed" points="330.0,326.0 120.0,326.0" data-evidence="263-280" />
<rect class="edge-label-bg" x="115.5" y="300.0" width="219.0" height="22" rx="3" />
<text class="edge-label" x="225.0" y="315.0">4. access + refresh + ID token</text>
<polyline class="edge kind-request style-solid emphasis-primary" points="120.0,388.0 540.0,388.0" data-evidence="303-322" />
<rect class="edge-label-bg" x="200.4" y="362.0" width="259.2" height="22" rx="3" />
<text class="edge-label" x="330.0" y="377.0">5. GET /api/me · Bearer access token</text>
<polyline class="edge kind-response style-dashed emphasis-normal semantic-dashed" points="540.0,450.0 120.0,450.0" data-evidence="342-376" />
<rect class="edge-label-bg" x="183.7" y="424.0" width="292.7" height="22" rx="3" />
<text class="edge-label" x="330.0" y="439.0">6. subject · username · issuer · audience</text>
</svg>