187 lines
5.2 KiB
JSON
187 lines
5.2 KiB
JSON
{
|
|
"version": "1.1",
|
|
"id": "ap1-browser-bearer-flow",
|
|
"title": "AP1 callback code에서 브라우저 Bearer 요청까지",
|
|
"question": "AP1에서 authorization code는 어떤 순서로 브라우저의 Bearer API 요청과 사용자 JSON이 되는가?",
|
|
"type": "sequence",
|
|
"direction": "LR",
|
|
"audience": [
|
|
"SPA OAuth와 Resource Server 경계를 추적하는 개발자"
|
|
],
|
|
"summary": "브라우저 SPA가 PKCE code를 token set으로 교환하고 access token을 직접 Bearer header로 조립해 Resource Server를 호출한다.",
|
|
"alt": "브라우저 SPA, Keycloak, Resource Server 사이에서 authorization request, callback, token 교환, Bearer API 호출과 JSON 응답이 이어지는 순서도.",
|
|
"long_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을 받는다.",
|
|
"source_context": {
|
|
"document": "document.md",
|
|
"document_sha256": "df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371",
|
|
"anchor": {
|
|
"kind": "marker",
|
|
"value": "ap1-browser-bearer-flow",
|
|
"line": 395
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "sequence",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-approval-sequence"
|
|
],
|
|
"rationale": "authorization redirect, callback, token 교환과 API 호출의 시간 순서가 핵심이므로 participant lifeline과 ordered message가 가장 적합하다.",
|
|
"focus_node": "browser"
|
|
},
|
|
"groups": [],
|
|
"nodes": [
|
|
{
|
|
"id": "browser",
|
|
"label": "브라우저 SPA",
|
|
"kind": "participant",
|
|
"role": "participant",
|
|
"emphasis": "primary",
|
|
"description": "PKCE transaction과 token set을 처리하고 access token으로 API 요청을 만드는 public client.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 201,
|
|
"end_line": 236
|
|
},
|
|
{
|
|
"start_line": 240,
|
|
"end_line": 280
|
|
},
|
|
{
|
|
"start_line": 303,
|
|
"end_line": 322
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "keycloak",
|
|
"label": "Keycloak",
|
|
"kind": "participant",
|
|
"role": "participant",
|
|
"description": "Authorization code를 발급하고 code와 verifier를 token set으로 교환하는 authorization server.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 223,
|
|
"end_line": 263
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "resource-server",
|
|
"label": "Resource Server",
|
|
"kind": "participant",
|
|
"role": "participant",
|
|
"description": "Bearer JWT의 signature, issuer, timestamp와 audience를 검증하고 사용자 JSON을 만드는 Spring API.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 311,
|
|
"end_line": 353
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "authorize",
|
|
"from": "browser",
|
|
"to": "keycloak",
|
|
"label": "authorization request · S256 challenge",
|
|
"kind": "request",
|
|
"order": 1,
|
|
"evidence": [
|
|
{
|
|
"start_line": 223,
|
|
"end_line": 236
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "callback",
|
|
"from": "keycloak",
|
|
"to": "browser",
|
|
"label": "callback · code + state",
|
|
"kind": "response",
|
|
"style": "dashed",
|
|
"order": 2,
|
|
"evidence": [
|
|
{
|
|
"start_line": 240,
|
|
"end_line": 250
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "token-request",
|
|
"from": "browser",
|
|
"to": "keycloak",
|
|
"label": "token request · code + verifier",
|
|
"kind": "request",
|
|
"order": 3,
|
|
"evidence": [
|
|
{
|
|
"start_line": 250,
|
|
"end_line": 261
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "token-response",
|
|
"from": "keycloak",
|
|
"to": "browser",
|
|
"label": "access + refresh + ID token",
|
|
"kind": "response",
|
|
"style": "dashed",
|
|
"order": 4,
|
|
"evidence": [
|
|
{
|
|
"start_line": 263,
|
|
"end_line": 280
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "api-request",
|
|
"from": "browser",
|
|
"to": "resource-server",
|
|
"label": "GET /api/me · Bearer access token",
|
|
"kind": "request",
|
|
"emphasis": "primary",
|
|
"order": 5,
|
|
"evidence": [
|
|
{
|
|
"start_line": 303,
|
|
"end_line": 322
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "api-response",
|
|
"from": "resource-server",
|
|
"to": "browser",
|
|
"label": "subject · username · issuer · audience",
|
|
"kind": "response",
|
|
"style": "dashed",
|
|
"order": 6,
|
|
"evidence": [
|
|
{
|
|
"start_line": 342,
|
|
"end_line": 376
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "로그인과 API 호출을 하나의 정확한 happy-path message sequence로 제한했다."
|
|
}
|
|
}
|