- 계약 채택 — 독자 질문, 후보 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>
4.2 KiB
id, kind, slug, title, topic, topicName, project, status, version, decisionStatus, decidedOn, studio, public, sourceRevision, source
| id | kind | slug | title | topic | topicName | project | status | version | decisionStatus | decidedOn | studio | public | sourceRevision | source | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 19b55c39-c583-4161-9775-df954280a568 | PROJECT_DECISION | bff-owns-token-when-browser-must-not | BFF가 OAuth Token을 관리하는 조건 | oauth-oidc-auth-boundary | OAuth/OIDC 인증 경계 | KeyCloak Patterns | 게시 중 | 23 | PROPOSED | 2026-08-31 | https://hyeonworks.com/studio/documents/19b55c39-c583-4161-9775-df954280a568/edit | https://hyeonworks.com/projects/keycloak-patterns/decisions/bff-owns-token-when-browser-must-not | keycloak-patterns-lab@2026-08 |
|
BFF가 OAuth Token을 관리하는 조건
BFF(Backend for Frontend)는 화면에 필요한 API를 브라우저 대신 호출하고 결과만 돌려주는 백엔드다. 애플리케이션 계층에서 API 응답 조합과 인가를 처리하면서도 브라우저 JavaScript에는 OAuth 토큰을 노출하지 않아야 한다면 이 구조를 선택할 수 있다.
이 경우 BFF가 authorization code를 토큰으로 교환해 액세스 토큰과 리프레시 토큰을 서버에 보관한다. 브라우저는 OAuth 토큰 대신 애플리케이션 세션으로 BFF를 호출하고, BFF는 보관한 액세스 토큰으로 downstream Resource Server를 호출한다.
근거
- Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정 이 결정대로 만든 구조를 실제로 실행해 본 기록이다.
- BFF 인증 구조 설계 기준 이 결정이 PROPOSED인 동안 실제로 따르는 기준이다.
- OAuth/OIDC 인증 패턴 선택 기준 이 결정을 적용할 조건과 피해야 할 조건을 갈라 놓은 기록이다.
- Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출 액세스 토큰이 브라우저로 나가서 이 요구를 만족하지 못한 경우다.
결정문
브라우저에 OAuth 토큰을 노출하지 않으면서 애플리케이션이 Resource Server 호출을 중계하고 조합해야 하는 경우, BFF가 authorization code 교환과 토큰 보관, downstream API 호출을 소유한다.
브라우저에는 애플리케이션 세션만 제공한다.
판단 이유
브라우저에 OAuth 토큰을 전달하지 않으려면 서버가 authorization code를 교환하고 액세스 토큰으로 downstream API를 호출해야 한다.
Mediator 구조에서는 브라우저가 Resource Server를 직접 호출하다 보니 액세스 토큰을 /token/access 응답으로 내보내고, 그래서 브라우저에 OAuth 토큰을 주지 않는다는 요구에는 맞지 않는다.
Forward-Auth 구조도 브라우저에 OAuth 토큰을 전달하지 않을 수 있지만, upstream은 JWT를 직접 검증하지 않고 edge가 넘겨준 identity header를 사용한다. 애플리케이션이 액세스 토큰으로 여러 Resource Server를 직접 호출하거나 사용자별 API 조합을 처리해야 한다면 BFF 쪽이 요구에 더 잘 맞는다.
처리량과 장애 복구 시간, session failover, secret rotation 절차는 확인하지 못해서 이 판단의 근거가 아니다.
영향
- BFF가 로그인 상태와 액세스 토큰, 리프레시 토큰을 보관하는 보안 구성요소가 되므로, 요청을 그대로 넘기는 프록시처럼 다루지 않는다.
- 상태를 바꾸는 요청마다 CSRF 검증이 붙는다. 클라이언트에 내려가는 값과 실제로 제출해야 하는 값이 다를 수 있어서 클라이언트 코드도 그 차이를 알아야 한다.
- 재시작과 replica 이동을 견딜 공유 저장소, 저장한 토큰의 암호화, 암호화 키 교체를 함께 설계해야 한다. 이 세 가지는 아직 정하지 못했다.
- 로그아웃은 애플리케이션 세션과 authorized client를 함께 지워야 하는데, 두 상태를 따로 보관하다 보니 한 번의 삭제로 둘이 같이 지워지지 않는다.
- 모든 UI 요청이 BFF를 지나므로 지연과 단일 장애 지점을 함께 준비해야 한다.
- 브라우저에서 토큰을 없애도 XSS는 막히지 않는다. 같은 origin에서 실행되는 악성 script는 사용자 세션으로 BFF를 호출할 수 있어서 XSS 방어는 따로 세워야 한다.