240 lines
6.2 KiB
JSON
240 lines
6.2 KiB
JSON
{
|
|
"version": "1.1",
|
|
"id": "bearer-jwt-validation-chain",
|
|
"title": "Bearer JWT가 검증과 역할 변환을 지나 principal이 된다",
|
|
"question": "Resource Server가 받은 Bearer JWT는 어떤 검증과 변환 단계를 지나 authenticated principal이 되는가?",
|
|
"type": "data-flow",
|
|
"direction": "TB",
|
|
"audience": [
|
|
"Spring Security Resource Server의 JWT 검증 경계를 확인하는 개발자"
|
|
],
|
|
"summary": "Bearer JWT가 JWK 서명, issuer·시간, audience 검증을 거친 뒤 realm role 변환을 통해 authenticated principal과 ROLE_* authorities가 된다.",
|
|
"alt": "Bearer JWT 입력이 JwtDecoder, issuer·시간 검증, audience 검증, role converter를 거쳐 authenticated principal이 되는 검증 사슬.",
|
|
"long_description": "왼쪽의 raw Bearer JWT가 NimbusJwtDecoder에서 JWK 서명을 확인한 뒤 issuer와 timestamp validator, 프로젝트의 AudienceValidator를 통과해 validated Jwt가 된다. 이어서 KeycloakRealmRoleConverter가 realm_access.roles를 ROLE_* authority로 바꾸고 authenticated principal이 만들어진다.",
|
|
"source_context": {
|
|
"document": "document.md",
|
|
"document_sha256": "ea10df24b892e2c57123a37a4b4f0d821e4f394353e6746f50df6a48342353e9",
|
|
"anchor": {
|
|
"kind": "heading",
|
|
"value": "AP1 완주: callback code가 브라우저 Bearer 요청이 되기까지",
|
|
"line": 227
|
|
}
|
|
},
|
|
"composition": {
|
|
"profile": "component-flow",
|
|
"diagram_only": true,
|
|
"reference_ids": [
|
|
"payment-event-flow"
|
|
],
|
|
"rationale": "검증 순서가 일곱 단계라 문서 폭에서 가로로 늘리지 않고 위에서 아래로 읽는 component-flow로 표현한다.",
|
|
"focus_node": "audience-validator"
|
|
},
|
|
"groups": [],
|
|
"nodes": [
|
|
{
|
|
"id": "bearer-input",
|
|
"label": "Bearer JWT",
|
|
"kind": "data",
|
|
"role": "source",
|
|
"shape": "box",
|
|
"description": "Resource Server가 Authorization header에서 받은 raw Bearer JWT.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 354,
|
|
"end_line": 359
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "jwt-decoder",
|
|
"label": "NimbusJwtDecoder",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"description": "JWK를 이용해 JWT 서명을 검증하는 decoder.",
|
|
"details": [
|
|
"JWK signature"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 359,
|
|
"end_line": 360
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "issuer-time",
|
|
"label": "Issuer · Time validators",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"description": "Expected issuer와 timestamp 조건을 확인하는 default validators.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 361,
|
|
"end_line": 361
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "audience-validator",
|
|
"label": "AudienceValidator",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"emphasis": "primary",
|
|
"description": "keycloak-pattern-api audience 포함 여부를 확인하는 project validator.",
|
|
"details": [
|
|
"keycloak-pattern-api"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 362,
|
|
"end_line": 370
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "validated-jwt",
|
|
"label": "Validated Jwt",
|
|
"kind": "data",
|
|
"role": "service",
|
|
"description": "서명, issuer·시간, audience 검증을 통과한 Jwt.",
|
|
"evidence": [
|
|
{
|
|
"start_line": 363,
|
|
"end_line": 363
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "role-converter",
|
|
"label": "Realm role converter",
|
|
"kind": "service",
|
|
"role": "service",
|
|
"description": "realm_access.roles 문자열에 ROLE_ prefix를 붙여 authority로 변환한다.",
|
|
"details": [
|
|
"KeycloakRealmRoleConverter"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 364,
|
|
"end_line": 370
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "principal",
|
|
"label": "Authenticated principal",
|
|
"kind": "actor",
|
|
"role": "sink",
|
|
"shape": "actor",
|
|
"description": "검증된 JWT와 ROLE_* authorities를 가진 인증 결과.",
|
|
"details": [
|
|
"ROLE_* authorities"
|
|
],
|
|
"evidence": [
|
|
{
|
|
"start_line": 365,
|
|
"end_line": 365
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"edges": [
|
|
{
|
|
"id": "e1",
|
|
"from": "bearer-input",
|
|
"to": "jwt-decoder",
|
|
"label": "decode",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 354,
|
|
"end_line": 360
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "e2",
|
|
"from": "jwt-decoder",
|
|
"to": "issuer-time",
|
|
"label": "signature ok",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 360,
|
|
"end_line": 361
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "e3",
|
|
"from": "issuer-time",
|
|
"to": "audience-validator",
|
|
"label": "claims",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 361,
|
|
"end_line": 362
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "e4",
|
|
"from": "audience-validator",
|
|
"to": "validated-jwt",
|
|
"label": "valid audience",
|
|
"kind": "data",
|
|
"emphasis": "primary",
|
|
"evidence": [
|
|
{
|
|
"start_line": 362,
|
|
"end_line": 363
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "e5",
|
|
"from": "validated-jwt",
|
|
"to": "role-converter",
|
|
"label": "realm roles",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 363,
|
|
"end_line": 364
|
|
}
|
|
],
|
|
"assumption": false
|
|
},
|
|
{
|
|
"id": "e6",
|
|
"from": "role-converter",
|
|
"to": "principal",
|
|
"label": "authorities",
|
|
"kind": "data",
|
|
"evidence": [
|
|
{
|
|
"start_line": 364,
|
|
"end_line": 365
|
|
}
|
|
],
|
|
"assumption": false
|
|
}
|
|
],
|
|
"legend": [],
|
|
"metadata": {
|
|
"rationale": "서명 검증과 API 대상 audience 검증, role 변환이 서로 다른 단계임을 한 흐름에서 구분한다."
|
|
}
|
|
}
|