refactor: 문서 개선 중

This commit is contained in:
donghyeon-ka
2026-09-21 14:30:55 +09:00
parent c93cdea150
commit 805a18f486
1497 changed files with 525837 additions and 59152 deletions
@@ -119,6 +119,6 @@ AP2에서는 mediator가 token을 받고 API는 브라우저가 부른다. AP3
confidential client는 여기에 클라이언트 인증을 더한다. AP3의 `bff-confidential`은 `client_secret_basic`으로 자기 클라이언트를 인증하면서 PKCE S256도 함께 쓴다. Spring Security에서는 authorization request를 만드는 resolver에 `OAuth2AuthorizationRequestCustomizers.withPkce()`를 장착한다. 그러면 프레임워크가 `state`와 verifier를 만든다.
클라이언트 설정에 S256을 강제하는 속성이 없고, 테스트도 authorization request의 challenge를 검사하지 않는다. Authorization Code Flow를 쓴다는 것까지 확인했, PKCE S256이 고정됐는지는 확인하지 않다.
반면 AP2의 `token-mediating-confidential`은 client 설정에 S256을 강제하지 않고 AP2 E2E도 authorization request의 challenge를 검사하지 않는다. 따라서 AP2는 Authorization Code를 사용하는 confidential client라는 사실까지 확인했으며, PKCE S256이 고정되었다고 기록하지 않다.
<!-- body:end -->
@@ -10,9 +10,13 @@ status: 게시 전
version: 4
basisVersion: Keycloak 26.7.0 · Spring Security OAuth2 Resource Server
studio: "https://hyeonworks.com/studio/documents/87000d59-b69f-4010-9481-0b71c8bde32d/edit"
assets:
- key: bearer-jwt-validation-chain
file: ../../../final/assets/bearer-jwt-validation-chain/bearer-jwt-validation-chain.svg
sourceRevision: keycloak-patterns-lab@2026-08
source:
- final/document.md#선택의-이유와-지킨-경계-ap1
- final/document.md#선택이-코드와-흐름에-반영되는-방식-ap1-완주
---
# Bearer JWT가 인증된 principal이 되기까지
@@ -47,17 +51,9 @@ Spring이 받는 것은 이 헤더에 실려 온 문자열 그대로다. 요청
## 변환 순서
직접 만든 코드가 지나는 순서는 다음과 같다.
검증과 변환은 다음 순서로 이어진다.
```text label="raw Bearer JWT가 principal이 되기까지"
raw Bearer JWT
→ NimbusJwtDecoder(JWK signature)
→ default issuer + timestamp validators
→ AudienceValidator("keycloak-pattern-api")
→ validated Jwt
→ KeycloakRealmRoleConverter
→ authenticated principal + ROLE_* authorities
```
![Bearer JWT 입력이 JwtDecoder, issuer·시간 검증, audience 검증, role converter를 거쳐 authenticated principal이 되는 검증 사슬.](../../../final/assets/bearer-jwt-validation-chain/bearer-jwt-validation-chain.svg)
헤더를 꺼내 JWT 인증 제공자에 넘기고 설정된 디코더를 부르는 일은 Spring OAuth2 Resource Server가 한다. 저장소의 코드는 Spring 내부 필터를 직접 만들지 않으므로, 이 순서에는 설정 DSL(Domain Specific Language, 설정 전용 문법)이 붙여 주는 부분과 직접 만들어 끼운 빈이 함께 들어 있다.
@@ -43,7 +43,7 @@ source:
| Local Storage | 유지 | 읽는다 | 붙지 않는다 |
| HttpOnly 쿠키 | 만료까지 유지 | 읽지 못한다 | 붙는다 |
네 곳 가운데 쿠키만 요청에 자동으로 붙기 때문에, 쿠키를 자격 증명으로 쓰면 그 요청이 사용자가 의도한 것인지 확인하는 CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 검증이 따라붙는다.
네 곳 가운데 쿠키만 요청에 자동으로 붙기 때문에, 쿠키를 자격 증명으로 쓰면 공격 사이트가 쿠키만 이용해 만든 cross-site forged request와 애플리케이션이 anti-CSRF token을 가지고 만든 요청을 구분하는 CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 검증이 따라붙는다.
## userStore와 stateStore를 나눈다
@@ -20,7 +20,7 @@ source:
# Cookie로 인증하는 요청에서 CSRF token이 하는 일
세션 쿠키는 브라우저가 요청마다 자동으로 붙이기 때문에, 상태를 바꾸는 요청이 사용자가 의도한 것인지 서버가 따로 확인해야 한다. CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 토큰이 그 확인을 맡고, SameSite는 브라우저가 쿠키를 언제 보낼지 정하는 별도의 정책이다.
세션 쿠키는 브라우저가 요청마다 자동으로 붙이기 때문에, 상태를 바꾸는 요청에는 쿠키만으로 만든 cross-site forged request를 구분할 별도 검증 값이 필요하다. CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조) 토큰은 예상한 anti-CSRF token이 함께 온 요청인지 검사하고, SameSite는 브라우저가 쿠키를 언제 보낼지 정하는 별도의 정책이다.
## 관계
@@ -100,7 +100,7 @@ theme=dark
## SameSite가 정하는 것과 CSRF token이 정하는 것
SameSite는 쿠키를 다른 사이트로 보낼지 브라우저가 정하는 정책이고, CSRF 토큰은 상태를 바꾸는 요청이 사용자의 의도인지 서버가 검증하는 애플리케이션 규약이다.
SameSite는 쿠키를 다른 사이트로 보낼지 브라우저가 정하는 정책이고, CSRF 토큰은 cookie가 자동 첨부되는 상태 변경 요청에 별도 검증 값을 요구해 cross-site forged request를 구분하는 애플리케이션 규약이다.
| | SameSite | CSRF 토큰 |
|---|---|---|
@@ -10,6 +10,9 @@ status: 게시 전
version: 4
basisVersion: Keycloak 26.7.0 identity brokering
studio: "https://hyeonworks.com/studio/documents/d99fdec9-fe9e-4e0f-a50b-6fb9b9ed5719/edit"
assets:
- key: idp-broker-upstream-downstream-boundary
file: ../../../final/assets/idp-broker-upstream-downstream-boundary/idp-broker-upstream-downstream-boundary.svg
sourceRevision: keycloak-patterns-lab@2026-08
source:
- final/document.md#선택이-코드와-흐름에-반영되는-방식-google-login
@@ -36,16 +39,9 @@ source:
사용자가 브로커 로그인 화면에서 어느 외부 IdP로 로그인할지 고르면 인증 왕복이 두 번 일어난다. 앞의 왕복은 브로커와 외부 IdP 사이에서, 뒤의 왕복은 애플리케이션과 브로커 사이에서 일어난다. 브로커보다 앞에 있는 외부 IdP 쪽을 upstream이라고 부른다.
먼저 브라우저가 외부 IdP에 authorization 요청을 보내고, 브로커는 돌아온 응답을 검증해 자기 realm의 사용자와 연결한다. 그다음 애플리케이션으로 나가는 값은 브로커가 다시 만든다. 무엇이 무엇으로 바뀌는지 순서로 적으면 이렇다.
먼저 브라우저가 외부 IdP에 authorization 요청을 보내고, 브로커는 돌아온 응답을 검증해 자기 realm의 사용자와 연결한다. 그다음 애플리케이션으로 나가는 값은 브로커가 다시 만든다. Upstream IdP와 애플리케이션 경계는 다음처럼 이어진다.
```text label="brokering 변환 순서"
Google identity assertion
→ Keycloak broker validation
→ provider alias + upstream sub로 account identity 결정
→ Keycloak local user/session
→ Keycloak authorization code
→ AP1·AP2·AP3·AP4 중 선택한 downstream 경계
```
![Upstream IdP의 identity assertion이 Keycloak broker에서 local identity와 Keycloak authorization code로 바뀐 뒤 기존 AP1·AP2·AP3·AP4 경계로 이어지는 흐름.](../../../final/assets/idp-broker-upstream-downstream-boundary/idp-broker-upstream-downstream-boundary.svg)
외부 IdP가 보낸 것은 첫 줄의 `Google identity assertion` 하나이고, 그 아래 `Keycloak local user/session``Keycloak authorization code`는 브로커가 만든다.