docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
43bccd08a8
commit
b2963105a8
+2
-1
@@ -3,7 +3,8 @@ id: 39fdf472-82c4-43ed-abec-73de672f08ae
|
||||
kind: REFERENCE
|
||||
slug: authorization-code-endpoint-credential-movement
|
||||
title: Authorization Code Flow의 Endpoint와 Credential 이동 기준
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 34
|
||||
|
||||
+11
-10
@@ -3,7 +3,8 @@ id: 97eddd97-1096-426a-a2c6-a6c5bf1cd09f
|
||||
kind: REFERENCE
|
||||
slug: bff-authentication-design-criteria
|
||||
title: BFF 인증 구조 설계 기준
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 21
|
||||
@@ -43,7 +44,7 @@ cookie가 credential이 되면 브라우저가 요청마다 자동으로 붙여
|
||||
Access Token과 Refresh Token은 BFF 서버의 Authorized Client에 보관한다.
|
||||
브라우저는 OAuth Token을 직접 사용하지 않고 Session Cookie를 이용해 BFF에 요청한다.
|
||||
|
||||
BFF는 이 Session을 확인한 뒤, 저장해 둔 Access Token으로 `Authorization: Bearer ...` 헤더를 새로 만들어 Resource Server를 호출한다. 브라우저가 보낸 Session Cookie는 Resource Server로 전달되지 않는다. Session Cookie는 브라우저와 BFF 사이의 Credential이고, Access Token은 BFF와 Resource Server 사이의 Credential이다.
|
||||
BFF는 이 Session을 확인한 뒤, 저장해 둔 Access Token으로 Authorization: Bearer ... 헤더를 새로 만들어 Resource Server를 호출한다. 브라우저가 보낸 Session Cookie는 Resource Server로 전달되지 않는다. Session Cookie는 브라우저와 BFF 사이의 Credential이고, Access Token은 BFF와 Resource Server 사이의 Credential이다.
|
||||
|
||||
### 2. cookie가 credential이면 상태 변경 요청에 CSRF 검증을 둔다
|
||||
|
||||
@@ -55,21 +56,21 @@ BFF 구조에서는 브라우저가 요청할 때 Session Cookie를 자동으로
|
||||
|
||||
이때 화면이나 응답 본문에 표시되는 CSRF Token과 실제 요청 Header에 넣어야 하는 값이 항상 같다고 생각하면 안 된다.
|
||||
응답 본문에 노출된 값이 별도의 처리를 거친 값이라면, 클라이언트는 실제 CSRF Cookie에서 값을 읽어 Header에 넣어야 한다.
|
||||
잘못된 값을 보내면 정상적인 요청이라도 CSRF 검증에 실패해 `403 Forbidden` 응답을 받게 된다.
|
||||
잘못된 값을 보내면 정상적인 요청이라도 CSRF 검증에 실패해 403 Forbidden 응답을 받게 된다.
|
||||
|
||||
`SameSite`와 CSRF Token도 서로 다른 역할을 한다.
|
||||
`SameSite`는 브라우저가 Cross-Site 요청에 Cookie를 전송할지 제한하는 정책이고, CSRF Token은 Cookie가 포함되어 들어온 상태 변경 요청이 정상적인 클라이언트에서 만들어졌는지를 확인하기 위한 값이다.
|
||||
SameSite와 CSRF Token도 서로 다른 역할을 한다.
|
||||
SameSite는 브라우저가 Cross-Site 요청에 Cookie를 전송할지 제한하는 정책이고, CSRF Token은 Cookie가 포함되어 들어온 상태 변경 요청이 정상적인 클라이언트에서 만들어졌는지를 확인하기 위한 값이다.
|
||||
또한 SameSite는 Origin이 아니라 Site를 기준으로 판단하므로, Origin은 다르지만 같은 Site에 속하는 요청도 존재할 수 있다.
|
||||
|
||||
### 3. session과 authorized client의 수명주기를 따로 설계한다
|
||||
|
||||
Application Session과 Authorized Client는 서로 다른 값을 저장하고 조회한다.
|
||||
Session은 `session ID`를 기준으로 조회하지만, Authorized Client는 `client registration 이름`과 `principal name`을 기준으로 조회한다.
|
||||
Session은 session ID를 기준으로 조회하지만, Authorized Client는 client registration 이름과 principal name을 기준으로 조회한다.
|
||||
따라서 여러 인스턴스에서 상태를 공유하기 위해 Shared Store를 도입할 때도 Session 저장소와 Authorized Client 저장소를 각각 어떻게 구성할지 확인해야 한다.
|
||||
|
||||
특히 Authorized Client의 조회 기준에는 `session ID`가 포함되지 않는다.
|
||||
특히 Authorized Client의 조회 기준에는 session ID가 포함되지 않는다.
|
||||
그래서 같은 사용자가 두 브라우저에서 동일한 Client로 로그인하면 두 Session이 같은 Authorized Client 정보를 사용하거나, 나중에 로그인하면서 저장된 Token 정보가 갱신될 수 있다.
|
||||
브라우저나 Session마다 서로 다른 Token을 유지해야 한다면 `session ID`까지 포함해 Token을 구분할 수 있도록 별도의 저장 구조를 설계해야 한다.
|
||||
브라우저나 Session마다 서로 다른 Token을 유지해야 한다면 session ID까지 포함해 Token을 구분할 수 있도록 별도의 저장 구조를 설계해야 한다.
|
||||
|
||||
운영 환경에서는 서버가 재시작되거나 요청이 다른 Replica로 전달되더라도 로그인 상태와 Token을 계속 사용할 수 있는지도 고려해야 한다. 이를 위해 Session과 Authorized Client를 공유 저장소에 보관할지, Session Affinity를 사용할지 등을 결정해야 한다.
|
||||
Token을 외부 저장소에 보관한다면 Access Token과 Refresh Token을 어떻게 보호할지도 정해야 하며, 저장 시 암호화한다면 암호화 Key의 보관 위치와 교체 방법까지 함께 설계해야 한다.
|
||||
@@ -81,8 +82,8 @@ Session과 Authorized Client는 조회 기준과 저장소가 다르므로, Logo
|
||||
### 4. Downstream 오류를 클라이언트 응답으로 변환한다
|
||||
|
||||
BFF가 Resource Server의 오류를 그대로 브라우저에 전달하면 화면에서는 오류의 원인을 일관되게 판단하기 힘들다.
|
||||
예를 들어 Resource Server에서 `401 Unauthorized`가 발생했다면 Access Token이 만료되었거나 더 이상 유효하지 않은 상황인지 확인하고, 필요한 경우 Token 갱신이나 재로그인으로 연결해야 한다.
|
||||
하지만 인증은 정상적으로 되었지만 해당 기능을 사용할 권한이 없어 `403 Forbidden`이 발생한 경우에는 권한 부족으로 처리해야 한다.
|
||||
예를 들어 Resource Server에서 401 Unauthorized가 발생했다면 Access Token이 만료되었거나 더 이상 유효하지 않은 상황인지 확인하고, 필요한 경우 Token 갱신이나 재로그인으로 연결해야 한다.
|
||||
하지만 인증은 정상적으로 되었지만 해당 기능을 사용할 권한이 없어 403 Forbidden이 발생한 경우에는 권한 부족으로 처리해야 한다.
|
||||
|
||||
Resource Server가 응답하지 않거나 처리가 지연되는 경우도 별도의 규칙이 필요하다.
|
||||
요청을 얼마 동안 기다릴지 Timeout을 정하고, 실패한 요청을 다시 시도할 수 있는 경우에는 Retry 정책을 적용한다.
|
||||
|
||||
+6
-5
@@ -3,7 +3,8 @@ id: 004dd0a2-5fb3-4f25-80c9-576f709de331
|
||||
kind: REFERENCE
|
||||
slug: forward-auth-identity-header-trust
|
||||
title: Forward-Auth에서 Identity Header를 신뢰하기 위한 조건
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 29
|
||||
@@ -59,20 +60,20 @@ public: "https://hyeonworks.com/references/forward-auth-identity-header-trust"
|
||||
|
||||
또한 신뢰할 수 있는 프록시의 범위도 필요한 대상만 포함하도록 제한한다.
|
||||
이 범위를 너무 넓게 설정하면 같은 내부 네트워크에 있는 다른 서비스가 신뢰받는 프록시처럼 요청을 보낼 수 있다.
|
||||
특히 `Forwarded`나 `X-Forwarded-*` 헤더를 신뢰하는 구조에서는 어떤 프록시의 요청까지 신뢰할지를 먼저 좁혀 둔다.
|
||||
특히 Forwarded나 X-Forwarded-* 헤더를 신뢰하는 구조에서는 어떤 프록시의 요청까지 신뢰할지를 먼저 좁혀 둔다.
|
||||
|
||||
### 3. auth endpoint는 subrequest 전용으로 둔다
|
||||
|
||||
이 Endpoint는 외부 사용자가 직접 호출하는 API가 아니라, 인증 과정에서 Proxy가 내부적으로 호출하기 위한 Endpoint다.
|
||||
따라서 외부 요청으로는 접근할 수 없게 하고 Proxy가 생성한 내부 요청만 허용해야 한다.
|
||||
|
||||
Nginx에서는 해당 Location에 `internal`을 설정해 외부에서 직접 호출하는 것을 차단할 수 있다.
|
||||
Nginx에서는 해당 Location에 internal을 설정해 외부에서 직접 호출하는 것을 차단할 수 있다.
|
||||
|
||||
### 4. upstream이 헤더 존재만 보지 않는다
|
||||
|
||||
요청이 신뢰할 수 있는 Proxy에서 전달된 것인지 확인하기 위해, 배포할 때 설정한 내부용 Credential과 요청에 포함된 Credential을 비교한다. 이때 Credential 값의 일부가 얼마나 일치하는지에 따라 비교 시간이 크게 달라지지 않는 안전한 비교 방식을 사용한다.
|
||||
|
||||
이 검증을 각 Controller에서 개별적으로 처리하면 새로운 Endpoint를 추가할 때 검증 로직을 빠뜨릴 수 있기 때문에 운영 환경에서는 `Filter`, `Interceptor`, `Security Chain`과 같은 공통 처리 지점에서 모든 대상 요청에 동일한 검증이 적용되도록 구성해야 한다.
|
||||
이 검증을 각 Controller에서 개별적으로 처리하면 새로운 Endpoint를 추가할 때 검증 로직을 빠뜨릴 수 있기 때문에 운영 환경에서는 Filter, Interceptor, Security Chain과 같은 공통 처리 지점에서 모든 대상 요청에 동일한 검증이 적용되도록 구성해야 한다.
|
||||
|
||||
### 5. Network 격리와 헤더 검증을 모두 적용한다
|
||||
|
||||
@@ -91,7 +92,7 @@ Role을 이용해 인가까지 처리하려면 Role 정보를 어떤 방식으
|
||||
|
||||
### 7. 요청 성공 여부가 아니라 전달된 사용자 정보를 확인한다
|
||||
|
||||
정상적으로 로그인된 세션에서 사용자 정보 헤더만 위조해 요청했다면, 세션 자체는 유효하므로 요청이 `200 OK`로 처리되는 것은 정상이다.
|
||||
정상적으로 로그인된 세션에서 사용자 정보 헤더만 위조해 요청했다면, 세션 자체는 유효하므로 요청이 200 OK로 처리되는 것은 정상이다.
|
||||
|
||||
테스트에서 확인해야 하는 것은 요청의 성공이나 실패가 아니라 애플리케이션이 어떤 사용자를 인증된 사용자로 인식했는지다.
|
||||
공격자가 임의로 넣은 사용자 정보가 아니라, 인증 프록시가 확인한 실제 사용자 정보가 사용되어야 한다.
|
||||
|
||||
+6
-5
@@ -3,7 +3,8 @@ id: 1a00a640-8987-4075-a9e4-7ec023cdffbb
|
||||
kind: REFERENCE
|
||||
slug: external-idp-federation-application-boundary
|
||||
title: 외부 IdP 연동과 Application 인증 구조의 경계
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 27
|
||||
@@ -51,12 +52,12 @@ Resource Server 역시 Keycloak이 발급한 Token을 검증한다.
|
||||
로그인 화면에서 Google이나 다른 Provider를 선택하게 하거나, Provider별 계정을 Keycloak 사용자와 어떻게 연결할지를 별도로 처리하는 것은 자연스럽다.
|
||||
하지만 Resource Server가 Google과 Keycloak의 Token을 각각 다르게 검증하거나, 애플리케이션의 인가 로직이 로그인에 사용한 Provider에 따라 달라지기 시작한다면 외부 IdP와 애플리케이션 사이를 분리하던 Keycloak의 역할이 제대로 유지되고 있는지 확인할 필요가 있다.
|
||||
|
||||
### 2. 외부 계정은 provider와 `subject` 조합으로 식별한다
|
||||
### 2. 외부 계정은 provider와 subject 조합으로 식별한다
|
||||
|
||||
이메일 주소는 변경될 수 있고 다른 계정과 중복될 가능성도 있기 때문에 외부 계정을 식별하고 연결하는 기준으로 사용하기에는 적절하지 않다.
|
||||
|
||||
대신 어떤 Provider에서 인증했는지와 해당 Provider가 사용자에게 부여한 고유 식별자(`subject`)를 함께 사용해 외부 계정을 식별한다.
|
||||
예를 들어 Google 사용자는 `Google + subject`의 조합으로 구분한다.
|
||||
대신 어떤 Provider에서 인증했는지와 해당 Provider가 사용자에게 부여한 고유 식별자(subject)를 함께 사용해 외부 계정을 식별한다.
|
||||
예를 들어 Google 사용자는 Google + subject의 조합으로 구분한다.
|
||||
|
||||
이메일만을 기준으로 계정을 연결하면 사용자가 이메일 주소를 변경했을 때 기존 계정과의 연결을 찾지 못하거나, 동일한 이메일을 가진 다른 계정을 잘못 연결할 수 있다.
|
||||
|
||||
@@ -93,6 +94,6 @@ Resource Server 역시 Keycloak이 발급한 Token을 검증한다.
|
||||
## 예시
|
||||
|
||||
- Google 로그인을 추가해도 애플리케이션이 고르는 것은 여전히 4가지 구조 중 하나다
|
||||
- 브로커는 `provider alias + upstream subject` 조합을 기준으로 외부 계정을 식별한다.
|
||||
- 브로커는 provider alias + upstream subject 조합을 기준으로 외부 계정을 식별한다.
|
||||
- 애플리케이션이 신뢰하는 issuer는 외부 IdP가 아니라 브로커다
|
||||
- mock OIDC provider로 확인한 것은 브로커와 claim mapping 계약까지다
|
||||
|
||||
+3
-2
@@ -3,7 +3,8 @@ id: 3f886154-1b85-407b-bda4-57d28370e745
|
||||
kind: REFERENCE
|
||||
slug: oauth-oidc-pattern-selection-criteria
|
||||
title: OAuth/OIDC 인증 패턴 선택 기준
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 23
|
||||
@@ -48,7 +49,7 @@ Forward-Auth 구조에서는 애플리케이션이 OAuth Token을 직접 관리
|
||||
|
||||
구조를 비교할 때는 브라우저의 Access Token 사용 여부, Resource Server 호출 주체, 서버에서 관리하는 인증 상태, Resource Server가 검증하는 Credential, CSRF 처리 위치를 확인한다.
|
||||
|
||||
SPA는 Bearer Access Token을 직접 `Authorization` Header에 넣어 Resource Server를 호출하고, 인증에 Cookie를 사용하지 않는다.
|
||||
SPA는 Bearer Access Token을 직접 Authorization Header에 넣어 Resource Server를 호출하고, 인증에 Cookie를 사용하지 않는다.
|
||||
|
||||
SPA와 Mediator에서는 브라우저가 Access Token을 사용해 Resource Server를 직접 호출한다.
|
||||
차이는 Mediator가 로그인 Session과 OAuth Token을 서버에서도 관리하고, 로그인 이후 브라우저에 Access Token을 전달한다는 점이다.
|
||||
|
||||
+11
-10
@@ -3,7 +3,8 @@ id: ede6b9ce-eeed-40c8-9175-9e8116029395
|
||||
kind: REFERENCE
|
||||
slug: public-confidential-client-boundary
|
||||
title: Public Client와 Confidential Client 구분 기준
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 27
|
||||
@@ -46,26 +47,26 @@ Client 종류는 secret을 안전하게 보관할 수 있는지로 정하고, To
|
||||
|
||||
### 1. secret을 숨길 수 있는지로 종류를 정한다
|
||||
|
||||
애플리케이션의 배포 파일이나 실행 중인 메모리에서 사용자가 `client secret`을 확인할 수 있다면 이를 안전하게 보관할 수 없으므로 Public Client로 본다.
|
||||
반대로 `client secret`을 서버 내부에만 보관하고 사용자에게 전달되지 않도록 통제할 수 있다면 Confidential Client로 구성할 수 있다.
|
||||
애플리케이션의 배포 파일이나 실행 중인 메모리에서 사용자가 client secret을 확인할 수 있다면 이를 안전하게 보관할 수 없으므로 Public Client로 본다.
|
||||
반대로 client secret을 서버 내부에만 보관하고 사용자에게 전달되지 않도록 통제할 수 있다면 Confidential Client로 구성할 수 있다.
|
||||
|
||||
Native App도 브라우저에서 실행되는 것은 아니지만 애플리케이션이 사용자 기기에 설치되기 때문에, 배포 파일을 분석하면 내부에 포함된 `client secret`을 확인할 수 있다. 따라서 Native App 역시 일반적으로 Public Client로 다룬다.
|
||||
Native App도 브라우저에서 실행되는 것은 아니지만 애플리케이션이 사용자 기기에 설치되기 때문에, 배포 파일을 분석하면 내부에 포함된 client secret을 확인할 수 있다. 따라서 Native App 역시 일반적으로 Public Client로 다룬다.
|
||||
|
||||
### 2. public client에서 Authorization Code Flow에 PKCE를 함께 쓴다
|
||||
|
||||
PKCE는 `client secret`을 대신해서 Client를 인증하는 방식이 아니다.
|
||||
PKCE는 client secret을 대신해서 Client를 인증하는 방식이 아니다.
|
||||
Authorization Code가 중간에 탈취되더라도 다른 사람이 그 Code를 Token으로 교환하기 어렵게 만드는 보호 장치다.
|
||||
|
||||
로그인을 시작할 때 Client는 임의의 `code_verifier`를 만들고, 이를 변환한 `code_challenge`를 Authorization Request에 함께 보낸다. 이후 Authorization Code를 Token으로 교환할 때 원래의 `code_verifier`를 제출한다.
|
||||
Authorization Server는 처음 받은 `code_challenge`와 비교하여 같은 요청에서 시작된 교환인지 확인한다.
|
||||
로그인을 시작할 때 Client는 임의의 code_verifier를 만들고, 이를 변환한 code_challenge를 Authorization Request에 함께 보낸다. 이후 Authorization Code를 Token으로 교환할 때 원래의 code_verifier를 제출한다.
|
||||
Authorization Server는 처음 받은 code_challenge와 비교하여 같은 요청에서 시작된 교환인지 확인한다.
|
||||
|
||||
이때 `S256` 방식을 사용한다. `plain` 방식은 `code_verifier` 자체가 `code_challenge`로 전달되기 때문에 Authorization Request를 관찰한 사람이 그 값을 그대로 알 수 있다. 반면 `S256`은 `code_verifier`를 SHA-256으로 변환한 값을 전달하므로 Authorization Request에 원래의 `code_verifier`가 노출되지 않는다.
|
||||
이때 S256 방식을 사용한다. plain 방식은 code_verifier 자체가 code_challenge로 전달되기 때문에 Authorization Request를 관찰한 사람이 그 값을 그대로 알 수 있다. 반면 S256은 code_verifier를 SHA-256으로 변환한 값을 전달하므로 Authorization Request에 원래의 code_verifier가 노출되지 않는다.
|
||||
|
||||
### 3. confidential client에도 PKCE를 함께 쓸 수 있다
|
||||
|
||||
Client 인증을 사용하는 Confidential Client에서도 PKCE는 함께 사용할 수 있다.
|
||||
Client 인증과 PKCE는 보호하는 대상이 다르기 때문이다.
|
||||
Client 인증은 Token Endpoint에 요청한 Client가 올바른 Client인지 확인하고, PKCE는 Authorization Code를 받은 주체가 로그인 시작 시 생성한 `code_verifier`를 가지고 있는지 확인한다. 그래서 두 방식을 같이 사용하면 서로 다른 구간을 각각 보호할 수 있다.
|
||||
Client 인증은 Token Endpoint에 요청한 Client가 올바른 Client인지 확인하고, PKCE는 Authorization Code를 받은 주체가 로그인 시작 시 생성한 code_verifier를 가지고 있는지 확인한다. 그래서 두 방식을 같이 사용하면 서로 다른 구간을 각각 보호할 수 있다.
|
||||
|
||||
### 4. public client에서는 implicit flow와 direct access grant를 끈다
|
||||
|
||||
@@ -82,7 +83,7 @@ Direct Access Grant는 애플리케이션이 사용자의 아이디와 비밀번
|
||||
Confidential Client가 Authorization Code를 Token으로 교환하더라도, 그 결과로 받은 Access Token을 다시 브라우저에 전달하는 구조를 만들 수 있다.
|
||||
즉, Confidential Client라고 해서 Token이 반드시 서버 내부에만 있는 것은 아니다.
|
||||
|
||||
Client 종류는 `client secret`을 어디에 안전하게 보관할 수 있는지를 나타낸다.
|
||||
Client 종류는 client secret을 어디에 안전하게 보관할 수 있는지를 나타낸다.
|
||||
반면 Access Token이 브라우저까지 전달되는지는 어느 계층이 실제 API 호출을 담당하도록 설계했는지에 따라 별도로 결정된다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
+9
-8
@@ -3,7 +3,8 @@ id: 66c18e42-116c-459f-86bd-b7e4bf394866
|
||||
kind: REFERENCE
|
||||
slug: oauth-token-application-session-boundary
|
||||
title: OAuth Token과 Application Session을 구분하는 기준
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
topic: oauth-oidc-auth-boundary
|
||||
topicName: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 중
|
||||
version: 33
|
||||
@@ -48,7 +49,7 @@ Logout과 만료 처리에서도 같은 구분이 필요하다. IdP의 SSO Sessi
|
||||
IdP SSO Session, OAuth Access Token, OAuth Refresh Token, Application Session Cookie, Proxy Session Cookie는 각각 생성하는 주체와 사용하는 목적이 다른 별개의 상태다.
|
||||
|
||||
로그와 진단 정보에서도 어떤 상태를 확인한 것인지 구체적으로 기록한다.
|
||||
예를 들어 단순히 `로그인 상태가 만료되었다`고 남기는 대신 `Application Session이 만료되었다`, `Access Token이 만료되었다`, `Proxy Session이 존재하지 않는다`처럼 실제 Session이나 Token의 종류를 명시한다.
|
||||
예를 들어 단순히 로그인 상태가 만료되었다고 남기는 대신 Application Session이 만료되었다, Access Token이 만료되었다, Proxy Session이 존재하지 않는다처럼 실제 Session이나 Token의 종류를 명시한다.
|
||||
|
||||
이렇게 이름을 구분해야 장애를 분석하거나 Logout과 만료 동작을 확인할 때 어떤 상태가 남아 있고 어떤 상태를 삭제하거나 갱신해야 하는지 정확하게 판단할 수 있다.
|
||||
|
||||
@@ -64,20 +65,20 @@ Proxy Session Cookie는 인증 Proxy가 발급하고, 이후 Proxy가 인증 상
|
||||
|
||||
### 3. 같은 사용자라도 Credential은 서로 다른 상태를 나타낸다
|
||||
|
||||
Application Session Cookie는 서버에 저장된 Session을 찾기 위한 `session ID`를 브라우저에 전달하는 데 사용한다.
|
||||
Application Session Cookie는 서버에 저장된 Session을 찾기 위한 session ID를 브라우저에 전달하는 데 사용한다.
|
||||
실제 Access Token과 Refresh Token은 Cookie 안에 들어 있는 것이 아니라 Authorized Client와 같은 별도의 서버 저장소에 보관된다. 따라서 Session Cookie와 OAuth Token 저장소는 서로 구분해서 봐야 한다.
|
||||
|
||||
Proxy Session Cookie는 반드시 같은 방식으로 동작하는 것은 아니다.
|
||||
별도의 서버 Session Store를 두지 않고, 인증 상태를 확인하는 데 필요한 정보를 Cookie 자체에 담은 뒤 Proxy가 Cookie의 유효성을 검증하는 방식으로 구성할 수도 있다.
|
||||
이 경우 Cookie는 서버에 저장된 Session을 조회하기 위한 `session ID`와는 역할이 다르다.
|
||||
이 경우 Cookie는 서버에 저장된 Session을 조회하기 위한 session ID와는 역할이 다르다.
|
||||
|
||||
### 4. 브라우저에 없는 것을 범위까지 적는다
|
||||
|
||||
브라우저 JavaScript에 OAuth Token을 전달하지 않는 구조에서도 브라우저에 인증과 관련된 상태는 남아 있을 수 있다.
|
||||
예를 들어 BFF 구조에서는 애플리케이션의 `HttpOnly` Session Cookie가 유지될 수 있고, IdP에서는 자신의 도메인에 SSO Session Cookie를 유지할 수 있다.
|
||||
예를 들어 BFF 구조에서는 애플리케이션의 HttpOnly Session Cookie가 유지될 수 있고, IdP에서는 자신의 도메인에 SSO Session Cookie를 유지할 수 있다.
|
||||
|
||||
따라서 단순히 `브라우저에 인증 정보가 없다`거나 `브라우저에 Credential이 없다`고 표현하면 안 된다.
|
||||
`브라우저 JavaScript에 Access Token과 Refresh Token을 노출하지 않는다`처럼 무엇이 없고 어느 범위에서 접근할 수 없는지를 적는다.
|
||||
따라서 단순히 브라우저에 인증 정보가 없다거나 브라우저에 Credential이 없다고 표현하면 안 된다.
|
||||
브라우저 JavaScript에 Access Token과 Refresh Token을 노출하지 않는다처럼 무엇이 없고 어느 범위에서 접근할 수 없는지를 적는다.
|
||||
|
||||
### 5. 영구 저장과 메모리 보관을 구분한다.
|
||||
|
||||
@@ -119,7 +120,7 @@ Application Session을 무효화하는 것과 Authorized Client에 저장된 Acc
|
||||
|
||||
- 하나의 요청 흐름 안에서 어떤 Session이나 Token을 의미하는지가 이미 명확한 경우에는 짧은 이름을 사용할 수 있다.
|
||||
다만 문서에서 처음 등장할 때는 전체 이름을 먼저 적어 어떤 상태를 의미하는지 명확하게 정의한다.
|
||||
이후 같은 문맥에서는 의미가 달라지지 않는 범위에서 `Session`, `Access Token`, `Refresh Token`처럼 줄여서 표현할 수 있다.
|
||||
이후 같은 문맥에서는 의미가 달라지지 않는 범위에서 Session, Access Token, Refresh Token처럼 줄여서 표현할 수 있다.
|
||||
- IdP를 쓰지 않고 애플리케이션이 자체 로그인만 하는 구조에는 SSO session과 access token, refresh token이 없다.
|
||||
|
||||
## 예시
|
||||
|
||||
Reference in New Issue
Block a user