172 lines
5.1 KiB
JSON
172 lines
5.1 KiB
JSON
{
|
|
"version": "1.1",
|
|
"id": "ap2-mediator-architecture",
|
|
"title": "AP2는 refresh custody와 API caller를 서로 다른 경계에 둔다",
|
|
"question": "AP2에서 server-held refresh token과 browser-held access token은 어떻게 Resource Server 직접 호출로 이어지는가?",
|
|
"type": "architecture",
|
|
"direction": "LR",
|
|
"audience": [
|
|
"Token mediator의 상태와 브라우저 API 계약을 설계하는 개발자"
|
|
],
|
|
"summary": "Mediator가 access와 refresh token을 server-side authorized-client state에 보관하지만 access token만 브라우저 memory로 내보내고, 브라우저가 Resource Server용 Bearer header를 만든다.",
|
|
"alt": "브라우저가 Spring mediator에서 access token만 받아 Resource Server를 직접 호출하고 refresh token은 authorized-client store에 남기는 AP2 split-custody 아키텍처.",
|
|
"long_description": "브라우저는 AP2_SESSION으로 confidential Spring mediator의 login state를 사용한다. Mediator는 code를 교환한 뒤 access와 refresh token을 server-side authorized-client service에 저장한다. 현재 access token의 값, type, expiry만 브라우저로 전달되고 refresh token은 server 경계에 남는다. 브라우저는 memory에 있는 access token으로 Resource Server용 Bearer header를 만든다.",
|
|
"source_context": {
|
|
"document": "document.md",
|
|
"document_sha256": "df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371",
|
|
"anchor": {
|
|
"kind": "marker",
|
|
"value": "ap2-mediator-architecture",
|
|
"line": 162
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "component-flow",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-event-flow"
|
|
],
|
|
"rationale": "AP2의 핵심은 browser session에서 mediator state로 들어간 요청이 access-only 응답으로 browser에 돌아와 Resource Server 입력으로 이어지는 split-custody 경로다.",
|
|
"focus_node": "mediator"
|
|
},
|
|
"groups": [],
|
|
"nodes": [
|
|
{
|
|
"id": "browser",
|
|
"label": "Browser",
|
|
"kind": "actor",
|
|
"role": "source",
|
|
"shape": "actor",
|
|
"description": "AP2_SESSION을 사용하고 전달받은 access token으로 API를 직접 호출하는 주체.",
|
|
"details": [
|
|
"HttpOnly AP2_SESSION",
|
|
"access token in memory"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 156,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "mediator",
|
|
"label": "Spring mediator",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"emphasis": "primary",
|
|
"description": "Confidential OAuth client로 code를 교환하고 browser에 access-only 응답을 제공한다.",
|
|
"details": [
|
|
"oauth2Login",
|
|
"confidential client"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 156,
|
|
"end_line": 158
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "authorized-client-store",
|
|
"label": "Authorized-client store",
|
|
"kind": "database",
|
|
"role": "store",
|
|
"shape": "database",
|
|
"description": "Access와 refresh token을 server-side state로 보관한다.",
|
|
"details": [
|
|
"access token",
|
|
"refresh token"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 156,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "api-resource-server",
|
|
"label": "Resource Server",
|
|
"kind": "service",
|
|
"role": "sink",
|
|
"description": "브라우저가 만든 Bearer header를 직접 받으며 downstream audience를 검증하는 API.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 156,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "browser-to-mediator",
|
|
"from": "browser",
|
|
"to": "mediator",
|
|
"label": "AP2_SESSION",
|
|
"kind": "request",
|
|
"evidence": [
|
|
{
|
|
"start_line": 158,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "mediator-to-store",
|
|
"from": "mediator",
|
|
"to": "authorized-client-store",
|
|
"label": "보관 · 현재 access 조회",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 156,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "mediator-to-browser",
|
|
"from": "mediator",
|
|
"to": "browser",
|
|
"label": "access_token · type · expiry",
|
|
"kind": "response",
|
|
"style": "dashed",
|
|
"emphasis": "primary",
|
|
"evidence": [
|
|
{
|
|
"start_line": 158,
|
|
"end_line": 158
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "browser-to-api",
|
|
"from": "browser",
|
|
"to": "api-resource-server",
|
|
"label": "Bearer header",
|
|
"kind": "request",
|
|
"emphasis": "primary",
|
|
"evidence": [
|
|
{
|
|
"start_line": 158,
|
|
"end_line": 160
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "Mediator state와 browser API caller를 별도 node로 분리해 AP1·AP3와 다른 custody topology를 드러냈다."
|
|
}
|
|
}
|