169 lines
4.9 KiB
JSON
169 lines
4.9 KiB
JSON
{
|
|
"version": "1.1",
|
|
"id": "ap1-direct-architecture",
|
|
"title": "AP1은 OAuth client와 token custody를 브라우저에 둔다",
|
|
"question": "AP1에서 code 교환, token 보관, Resource Server JWT 입력은 어느 구성 요소에 놓이는가?",
|
|
"type": "architecture",
|
|
"direction": "LR",
|
|
"audience": [
|
|
"SPA OAuth와 Resource Server 경계를 설계하는 개발자"
|
|
],
|
|
"summary": "Public SPA가 PKCE code 교환을 수행하고 token set을 JavaScript memory에 보관한 뒤 access JWT를 Resource Server 입력으로 사용한다.",
|
|
"alt": "SPA, Keycloak, 브라우저 JavaScript memory, Resource Server가 왼쪽에서 오른쪽으로 연결된 AP1 직접 인증 아키텍처.",
|
|
"long_description": "왼쪽의 public SPA가 Keycloak과 Authorization Code 및 PKCE S256 계약을 수행한다. Keycloak token 응답의 access, refresh, ID token은 브라우저 JavaScript memory에 놓이며, 그중 access JWT가 오른쪽 Resource Server의 검증 입력이 된다. Resource Server는 issuer, 시간 제약과 keycloak-pattern-api audience를 검증한다.",
|
|
"source_context": {
|
|
"document": "document.md",
|
|
"document_sha256": "df4d1a604c74e756672b5b40510abfedb8c67b39af280a5f51985ea9972f5371",
|
|
"anchor": {
|
|
"kind": "marker",
|
|
"value": "ap1-direct-architecture",
|
|
"line": 152
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "component-flow",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-event-flow"
|
|
],
|
|
"rationale": "AP1의 핵심은 SPA에서 시작한 code와 token이 브라우저 memory를 거쳐 Resource Server JWT 입력으로 이동하는 방향성 있는 구성 요소 경로다.",
|
|
"focus_node": "browser-spa"
|
|
},
|
|
"groups": [],
|
|
"nodes": [
|
|
{
|
|
"id": "browser-spa",
|
|
"label": "Public SPA",
|
|
"kind": "service",
|
|
"role": "source",
|
|
"emphasis": "primary",
|
|
"description": "Authorization Code와 PKCE를 직접 다루고 access JWT로 Resource Server를 호출하는 public OAuth client.",
|
|
"details": [
|
|
"spa-public",
|
|
"PKCE S256"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 148
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "browser-token-memory",
|
|
"label": "Browser JS memory",
|
|
"kind": "database",
|
|
"role": "store",
|
|
"shape": "box",
|
|
"description": "Access, refresh, ID token을 실행 중 보관하며 reload 뒤에는 복구하지 않는 browser-side custody.",
|
|
"details": [
|
|
"access · refresh · ID token",
|
|
"persistent Web Storage 없음"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 148,
|
|
"end_line": 150
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "keycloak",
|
|
"label": "Keycloak",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"description": "SPA의 code 교환 상대이며 API가 검증하는 JWT 서명의 출처.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 146
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "api-resource-server",
|
|
"label": "Resource Server",
|
|
"kind": "service",
|
|
"role": "sink",
|
|
"description": "Access JWT의 issuer, 시간 제약과 audience를 검증하는 API.",
|
|
"details": [
|
|
"issuer · time",
|
|
"keycloak-pattern-api audience"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 150
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "spa-to-keycloak",
|
|
"from": "browser-spa",
|
|
"to": "keycloak",
|
|
"label": "Authorization Code + PKCE S256",
|
|
"kind": "request",
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 146
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "keycloak-to-spa",
|
|
"from": "keycloak",
|
|
"to": "browser-spa",
|
|
"label": "access · refresh · ID token",
|
|
"kind": "response",
|
|
"style": "dashed",
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 148
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "spa-to-memory",
|
|
"from": "browser-spa",
|
|
"to": "browser-token-memory",
|
|
"label": "token set 보관",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 148,
|
|
"end_line": 150
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "spa-to-resource-server",
|
|
"from": "browser-spa",
|
|
"to": "api-resource-server",
|
|
"label": "memory-held access JWT · Bearer",
|
|
"kind": "request",
|
|
"emphasis": "primary",
|
|
"evidence": [
|
|
{
|
|
"start_line": 144,
|
|
"end_line": 150
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "로그인 message 순서가 아니라 OAuth client, token custody, JWT 검증 위치를 한 수준의 component path로 표현했다."
|
|
}
|
|
} |