docs(keycloak): adopt the decomposition contract, fix the redirect URI, strip evaluative prose
- 계약 채택 — 독자 질문, 후보 29건(PROMOTE 24 · MERGE_INTO 4 · KEEP_IN_SSOT 1). 게시 중 17건은 전부 유지. 저장소 keycloak-pattern 은 패턴 넷이 브랜치로 갈라져 있어 revisions 로 tip 넷을 적었다. keycloak-session-store 는 같은 저장소 @ cdac9b8 - 게시된 기록의 redirect_uri 가 SSOT·코드와 달랐다 — OAuth2callback.html → callback.html (frontend/src/app.js 에서 확인). 계약 title 이 기록과 다른 7건도 기록 쪽으로 맞췄다 - 미작성 1건 작성 — 패턴 검증을 실제로 돌릴 때의 안전한 순서(Reference) - 리뷰 100건 반영 — 설명 뒤에 붙은 평가·차례 예고·독자 오해 가정·작성 지시를 지웠다. 삭제가 남긴 조각 4건을 고치고, 원래부터 잘려 있던 로컬 미리보기 라벨 1건도 닫았다 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
62520a4dce
commit
4d50bb939a
+36
-50
@@ -11,91 +11,77 @@ version: 23
|
||||
verifiedOn: 2026-08-30
|
||||
studio: "https://hyeonworks.com/studio/documents/3f886154-1b85-407b-bda4-57d28370e745/edit"
|
||||
public: "https://hyeonworks.com/references/oauth-oidc-pattern-selection-criteria"
|
||||
sourceRevision: keycloak-patterns-lab@2026-08
|
||||
source:
|
||||
- final/document.md#검토한-선택지와-막힌-지점-책임과-데이터
|
||||
- final/document.md#얻은-것-잃은-것-적용하지-않을-때-사다리가-아니라
|
||||
- final/document.md#얻은-것-잃은-것-적용하지-않을-때-변경-경로
|
||||
---
|
||||
|
||||
# OAuth/OIDC 인증 패턴 선택 기준
|
||||
|
||||
SPA, Mediator, BFF, OAuth2-Proxy는 Token과 인증 상태를 처리하는 방식이 서로 다르다.
|
||||
브라우저가 Access Token을 직접 사용하는지, 실제 Resource Server를 누가 호출하는지, 서버에서 어떤 인증 상태를 보관하는지, Resource Server가 어떤 Credential을 검증하는지, CSRF를 어느 계층에서 처리하는지를 비교할 수 있다.
|
||||
네 구조를 안전한 순서로 줄 세우지 않고, 애플리케이션의 요구사항과 배포·운영 환경에 맞춰 고른다.
|
||||
SPA(Single Page Application), Mediator, BFF(Backend for Frontend), Forward-Auth는 토큰과 인증 상태를 다루는 방식이 서로 다르다. 브라우저가 액세스 토큰을 직접 쓰는지, Resource Server를 누가 호출하는지, 서버가 어떤 인증 상태를 보관하는지, Resource Server가 어떤 자격 증명을 검증하는지, CSRF(Cross-Site Request Forgery)를 어느 계층에서 처리하는지를 나란히 놓고 비교할 수 있다. 애플리케이션의 요구사항과 배포·운영 환경에 맞춰 고른다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계**
|
||||
브라우저가 code 교환과 token 보관, API 호출을 모두 맡는다.
|
||||
브라우저가 인가 코드를 토큰으로 바꾸고, 그 토큰을 들고 있다가, API까지 직접 호출한다.
|
||||
- **Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출**
|
||||
mediator가 refresh token을 관리하고 브라우저가 access token으로 API를 직접 호출하는 구성을 확인했다.
|
||||
Mediator가 리프레시 토큰을 서버에 두는데, 브라우저는 넘겨받은 액세스 토큰으로 Resource Server를 직접 호출한다.
|
||||
- **Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정**
|
||||
BFF가 code 교환, token 보관, Resource Server 호출을 모두 처리하는 구성을 확인했다.
|
||||
BFF가 인가 코드 교환과 토큰 보관, Resource Server 호출을 모두 처리하고 브라우저는 세션 쿠키만 받는다.
|
||||
- **Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유**
|
||||
인증이 edge로 가면 보호 자원이 검증하는 것이 JWT에서 헤더로 바뀐다.
|
||||
인증을 엣지로 옮기면 보호 자원이 검증하는 대상이 JWT에서 헤더로 바뀐다.
|
||||
|
||||
## 목적
|
||||
|
||||
브라우저에 OAuth Token이 노출되는 정도만 놓고 보면 구조별 차이는 있다.
|
||||
Token을 다른 위치로 옮기면 브라우저에 노출되는 범위가 달라지고, 그 Token을 맡게 된 계층에서 처리해야 할 항목이 늘어난다.
|
||||
브라우저에 OAuth 토큰이 노출되는 정도만 놓고 보면 구조마다 차이가 난다. 다만 토큰을 다른 계층으로 옮기면 브라우저에 노출되는 범위가 달라지는데, 그 토큰을 맡은 계층에서는 처리해야 할 항목이 늘어난다.
|
||||
|
||||
예를 들어 BFF는 OAuth Token을 서버에 보관해 브라우저에서 Token 원문을 제거할 수 있다.
|
||||
하지만 서버가 Session과 Authorized Client를 관리해야 하므로 Session 보호, CSRF 방어, 공유 저장소와 같은 새로운 설계가 필요해진다.
|
||||
예를 들어 BFF는 OAuth 토큰을 서버에 보관해 브라우저에서 토큰 원문을 없앨 수 있다. 하지만 그러려면 서버가 세션과 Authorized Client를 관리해야 한다. Authorized Client는 서버가 액세스 토큰과 리프레시 토큰을 보관하는 곳이다. 그래서 세션 보호와 CSRF 방어, 공유 저장소 같은 설계가 새로 필요해진다.
|
||||
|
||||
Forward-Auth 구조에서는 애플리케이션이 OAuth Token을 직접 관리하는 책임을 더 줄일 수 있다.
|
||||
대신 애플리케이션이 Edge에서 전달된 사용자 정보 헤더를 신뢰하게 되므로, 헤더 위조 방지와 직접 접근 차단, 신뢰할 수 있는 네트워크 경계를 구성해야 한다.
|
||||
Forward-Auth 구조에서는 애플리케이션이 OAuth 토큰을 직접 관리하는 책임을 더 줄일 수 있다. 대신 애플리케이션이 엣지에서 넘어온 사용자 정보 헤더를 신뢰하게 되므로, 헤더 위조 방지와 직접 접근 차단, 신뢰할 수 있는 네트워크 경계를 구성해야 한다.
|
||||
|
||||
그래서 어느 구조가 더 안전한지를 먼저 정하지 않고, 요구사항별로 무엇을 확인해야 하는지를 본다.
|
||||
어느 구조가 더 안전한지를 먼저 정하지 않고, 요구사항마다 무엇을 확인해야 하는지를 본다.
|
||||
|
||||
## 규칙
|
||||
|
||||
### 1. 다섯 항목으로 구조를 비교한다
|
||||
|
||||
구조를 비교할 때는 브라우저의 Access Token 사용 여부, Resource Server 호출 주체, 서버에서 관리하는 인증 상태, Resource Server가 검증하는 Credential, CSRF 처리 위치를 확인한다.
|
||||
브라우저가 액세스 토큰을 쓰는지, Resource Server를 누가 호출하는지, 서버가 어떤 인증 상태를 관리하는지, Resource Server가 어떤 자격 증명을 검증하는지, CSRF를 어디에서 처리하는지를 확인한다.
|
||||
|
||||
SPA는 Bearer Access Token을 직접 Authorization Header에 넣어 Resource Server를 호출하고, 인증에 Cookie를 사용하지 않는다.
|
||||
이 다섯 항목은 패턴 이름 대신 요청 하나를 끝까지 따라가서 채운다. 실제 엔드포인트와 메서드, 중간에 생기는 데이터, 성공 응답과 실패 응답까지 봐야 한다. 같은 질문을 로그인할 때와 로그인 뒤 API를 부를 때 각각 던진다.
|
||||
|
||||
SPA와 Mediator에서는 브라우저가 Access Token을 사용해 Resource Server를 직접 호출한다.
|
||||
차이는 Mediator가 로그인 Session과 OAuth Token을 서버에서도 관리하고, 로그인 이후 브라우저에 Access Token을 전달한다는 점이다.
|
||||
SPA와 Mediator에서는 브라우저가 액세스 토큰으로 Resource Server를 직접 호출한다. SPA는 Bearer 액세스 토큰을 Authorization 헤더에 직접 넣고 인증에는 쿠키를 쓰지 않는다. Mediator는 로그인 세션과 OAuth 토큰을 서버에서도 관리하고, 로그인이 끝나면 브라우저에 액세스 토큰을 전달한다.
|
||||
|
||||
BFF에서는 브라우저가 Session Cookie로 BFF를 호출하고, BFF가 서버에 저장된 Access Token을 사용해 Resource Server를 호출한다. 따라서 브라우저에는 OAuth Token을 전달하지 않지만 Session과 Authorized Client를 서버에서 관리해야 한다.
|
||||
BFF에서는 브라우저가 세션 쿠키로 BFF를 호출하고, BFF가 서버에 저장한 액세스 토큰으로 Resource Server를 호출한다. 그래서 브라우저에는 OAuth 토큰을 전달하지 않지만 세션과 Authorized Client를 서버에서 관리해야 한다.
|
||||
|
||||
Forward-Auth에서는 인증 Proxy가 Session을 관리하고, 인증이 완료된 요청에 사용자 정보를 추가해 애플리케이션으로 전달한다. 애플리케이션이 이 정보를 인증 근거로 사용한다면 Edge가 전달한 헤더를 신뢰할 수 있도록 직접 접근 차단, 헤더 덮어쓰기, 내부 Credential 검증과 같은 별도의 보호가 필요하다.
|
||||
Forward-Auth에서는 인증 프록시가 세션을 관리하고, 인증이 끝난 요청에 사용자 정보를 붙여 애플리케이션으로 넘긴다. 애플리케이션이 이 정보를 인증 근거로 쓴다면 엣지가 붙인 헤더를 믿을 수 있도록 직접 접근 차단과 헤더 덮어쓰기, 내부 자격 증명 검증 같은 보호를 따로 둬야 한다.
|
||||
|
||||
### 2. 피해야 할 조건을 먼저 확인한다
|
||||
|
||||
구조를 비교하기 전에 먼저 반드시 지켜야 하는 보안 요구사항을 확인한다.
|
||||
정책상 OAuth 토큰을 브라우저에 둘 수 없다면, 토큰을 Local Storage 대신 JavaScript 메모리에만 보관해도 요구사항을 채우지 못한다. 저장 위치만 달라졌을 뿐 브라우저 JavaScript가 여전히 토큰을 직접 다루기 때문이다. 이때는 브라우저가 액세스 토큰을 받는 SPA와 현재의 Mediator 구조를 선택 대상에서 뺀다.
|
||||
|
||||
정책상 OAuth Token을 브라우저에 둘 수 없다면 Token을 Local Storage 대신 JavaScript Memory에만 보관하는 것으로는 요구사항을 충족할 수 없다.
|
||||
저장 위치가 달라졌을 뿐 브라우저 JavaScript가 여전히 Token을 직접 다루기 때문이다.
|
||||
이 경우 브라우저가 Access Token을 받는 SPA나 현재의 Mediator 구조는 선택 대상에서 제외한다.
|
||||
|
||||
마찬가지로 애플리케이션에 직접 접근하는 경로를 차단할 수 없거나 외부에서 전달된 사용자 정보 헤더를 Edge에서 확실하게 제거하거나 덮어쓸 수 없다면, Edge가 전달한 사용자 정보를 인증 근거로 사용하는 구조는 선택하지 않는다.
|
||||
마찬가지로 애플리케이션으로 바로 들어오는 경로를 막을 수 없거나, 밖에서 들어온 사용자 정보 헤더를 엣지에서 확실히 지우거나 덮어쓸 수 없다면, 엣지가 전달한 사용자 정보를 인증 근거로 쓰는 구조는 고르지 않는다.
|
||||
|
||||
### 3. 선택 조건과 운영 책임을 같이 문서화한다
|
||||
|
||||
어떤 인증 구조를 선택했는지만 기록하지 않는다. 어떤 보안 요구사항과 운영 조건 때문에 해당 구조를 선택했는지 함께 기록한다.
|
||||
어떤 인증 구조를 골랐는지만 적지 않는다. 어떤 보안 요구사항과 운영 조건 때문에 그 구조를 골랐는지 함께 적는다.
|
||||
|
||||
또한 해당 구조를 적용하기 어려운 조건도 남긴다.
|
||||
예를 들어 브라우저에 OAuth Token을 둘 수 없는 환경에서는 SPA를 선택하기 어렵고, 애플리케이션의 직접 접근 경로나 사용자 정보 헤더를 안전하게 통제할 수 없는 환경에서는 Forward-Auth 구조를 적용하기 어렵다.
|
||||
|
||||
이렇게 선택 이유와 적용할 수 없는 조건을 함께 기록해야 이후 요구사항이나 운영 환경이 변경되었을 때
|
||||
기존 선택이 여전히 유효한지 다시 판단할 수 있다.
|
||||
그 구조를 적용하기 어려운 조건도 같이 적는다. 브라우저에 OAuth 토큰을 둘 수 없는 환경에서는 SPA를 고르기 어렵다. 애플리케이션으로 바로 들어오는 경로나 사용자 정보 헤더를 안전하게 통제할 수 없는 환경에서는 Forward-Auth를 적용하기 어렵다.
|
||||
|
||||
### 4. 이름으로 운영 속성을 추정하지 않는다
|
||||
|
||||
실제 운영에 적용할 때는 서버가 재시작되거나 특정 인스턴스에 장애가 발생해도 로그인 상태를 유지할 수 있는지,
|
||||
여러 Replica가 필요한 Session과 Token 정보를 공유할 수 있는지,
|
||||
저장소 장애가 발생했을 때 어떻게 복구할지 등을 별도로 확인해야 한다.
|
||||
내부 Credential이나 암호화 Key와 같은 Secret을 안전하게 보관하고 교체할 수 있는지도 함께 검증해야 한다.
|
||||
구조를 고를 때 이런 운영 항목까지 같이 적는다.
|
||||
실제 운영에 적용할 때는 서버가 재시작되거나 특정 인스턴스에 장애가 나도 로그인 상태를 유지할 수 있는지, 여러 레플리카가 필요한 세션과 토큰 정보를 공유할 수 있는지, 저장소 장애가 났을 때 어떻게 복구할지를 따로 확인해야 한다.
|
||||
|
||||
### 5. Credential의 위치가 바뀌면 저장·전달·검증 주체도 바뀐다
|
||||
내부 자격 증명이나 암호화 키와 같은 비밀값을 안전하게 보관하고 교체할 수 있는지도 함께 검증한다. 구조를 고를 때 이런 운영 항목까지 같이 적는다.
|
||||
|
||||
인증 패턴을 변경하면 Credential의 위치만 달라지는 것이 아니라, Credential을 저장하고 전달하고 검증하는 주체도 함께 바뀐다.
|
||||
따라서 패턴을 변경할 때는 기존 책임이 어느 계층으로 이동하는지까지 확인해야 한다.
|
||||
### 5. 자격 증명의 위치가 바뀌면 저장·전달·검증 주체도 바뀐다
|
||||
|
||||
예를 들어 Forward-Auth 구조에서는 Edge가 인증된 사용자 정보를 Header로 애플리케이션에 전달할 수 있다.
|
||||
처음에는 사용자 이름이나 이메일처럼 인증에 필요한 정보만 전달하더라도, 애플리케이션의 요구사항이 늘어나면서 Role이나 권한, 도메인에 종속된 사용자 정보까지 Header에 계속 추가될 수 있다.
|
||||
패턴을 바꿀 때는 기존 책임이 어느 계층으로 옮겨 가는지까지 확인해야 한다.
|
||||
|
||||
이처럼 Edge가 전달해야 하는 정보가 계속 늘어나고 애플리케이션의 인가 판단이나 화면에 필요한 여러 API 응답의 조합까지 필요해진다면,
|
||||
해당 책임을 Edge에 계속 추가하기보다 BFF에서 인가와 API 호출을 처리하는 구조가 더 적절한지 다시 검토한다.
|
||||
예를 들어 Forward-Auth 구조에서는 엣지가 인증된 사용자 정보를 헤더로 애플리케이션에 전달할 수 있다. 처음에는 사용자 이름이나 이메일처럼 인증에 필요한 정보만 전달하더라도, 애플리케이션의 요구사항이 늘면서 역할이나 권한, 도메인에 묶인 사용자 정보까지 헤더에 계속 붙을 수 있다.
|
||||
|
||||
엣지가 전달해야 하는 정보가 이렇게 늘어나고, 인가 판단이나 화면에 필요한 여러 API 응답의 조합까지 애플리케이션에 필요해진다면, 그 책임을 엣지에 계속 얹기보다 BFF에서 인가와 API 호출을 처리하는 구조가 더 맞는지 다시 검토한다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
@@ -105,12 +91,12 @@ Forward-Auth에서는 인증 Proxy가 Session을 관리하고, 인증이 완료
|
||||
|
||||
## 예외
|
||||
|
||||
- 요구가 하나로 좁혀지면 비교가 필요 없다. 브라우저에 token을 둘 수 없고 backend가 API를 조합해야 하면 선택지는 하나다.
|
||||
- 학습이나 시연이 목적이면 운영 속성 비교를 하지 않아도 된다.
|
||||
- 브라우저에 토큰을 둘 수 없고 서버가 API를 조합해야 하면 남는 선택지는 하나다.
|
||||
- 학습이나 시연이 목적이면 운영 속성까지 비교하지 않아도 된다.
|
||||
|
||||
## 예시
|
||||
|
||||
- SPA : 브라우저가 code 교환과 token 보관, API 호출을 모두 맡는다
|
||||
- Mediator : refresh token은 server에 있고 access token은 응답 본문으로 브라우저에 반환한다.
|
||||
- BFF : server가 code 교환·token 관리·API 호출을 담당하고 브라우저는 session cookie로 BFF를 호출한다
|
||||
- Forward-Auth : edge가 인증하고 upstream은 edge가 붙인 헤더를 본다
|
||||
- SPA: 브라우저가 인가 코드 교환과 토큰 보관, API 호출을 모두 맡는다.
|
||||
- Mediator: 리프레시 토큰은 서버에 두고, 액세스 토큰은 응답 본문으로 브라우저에 돌려준다.
|
||||
- BFF: 서버가 인가 코드 교환과 토큰 관리, API 호출을 맡고 브라우저는 세션 쿠키로 BFF를 호출한다.
|
||||
- Forward-Auth: 엣지가 인증하고 애플리케이션은 엣지가 붙인 헤더를 본다.
|
||||
|
||||
Reference in New Issue
Block a user