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
+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 정책을 적용한다.
|
||||
|
||||
Reference in New Issue
Block a user