refactor: 문서 개선 중
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
---
|
||||
id: 18a5cde2-dd1e-4bff-9f1c-997577ae438f
|
||||
kind: QUESTION
|
||||
slug: bff-session-authorized-client-store
|
||||
title: BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가
|
||||
topic: OAuth/OIDC 인증 경계
|
||||
project: KeyCloak Patterns
|
||||
status: 게시 전
|
||||
version: 8
|
||||
questionStatus: OPEN
|
||||
studio: "https://hyeonworks.com/studio/documents/18a5cde2-dd1e-4bff-9f1c-997577ae438f/edit"
|
||||
---
|
||||
|
||||
# BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가
|
||||
|
||||
session과 authorized client는 찾는 열쇠가 달라서 같은 저장소에 두는 것이 당연하지 않다. 저장소 후보는 Redis 쪽으로 기울어 있지만 token 암호화와 만료 정합, logout 정리를 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가**
|
||||
이 질문에서 저장소 부분만 떼어 낸 것이다.
|
||||
- **BFF에서 OAuth Token을 관리할 때 Session과 CSRF를 처리한 과정**
|
||||
session과 authorized client의 열쇠가 다르다는 사실의 출처다.
|
||||
- **BFF 인증 구조 설계 기준**
|
||||
이 기준의 저장소 항목이 이 질문의 답을 기다린다.
|
||||
- **Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가**
|
||||
저장소를 공유한 뒤에야 replica 경쟁이 재현된다.
|
||||
|
||||
## 사실
|
||||
|
||||
- 현재 구성에 Spring Session과 Redis, JDBC repository, 암호화 token store가 없다.
|
||||
- 현재 HttpSession은 servlet container의 in-memory 구현을 사용하므로 해당 process가 종료되면 session 데이터도 유지되지 않는다.
|
||||
- OAuth2AuthorizedClientService도 자동구성이 고르는 in-memory 구현이고 코드가 직접 선언하지 않는다.
|
||||
- session은 session ID로 조회하고 authorized client는 registration 이름과 principal name으로 조회한다. 두 저장 구조를 shared store로 전환할 때 각각 따로 설계해야 한다.
|
||||
- authorized client manager에 authorization-code와 refresh-token provider가 함께 구성돼 있어서, 저장소를 공유하게 되면 여러 인스턴스가 같은 항목을 동시에 갱신할 수 있게 된다.
|
||||
|
||||
## 가정
|
||||
|
||||
- 두 상태를 같은 저장소에 둘 필요는 없다.
|
||||
- 저장된 refresh token을 평문으로 두면 안 된다.
|
||||
- session 만료와 token 만료 중 하나가 먼저 오게 되면 그 순간의 동작이 정의돼 있어야 한다.
|
||||
|
||||
## 미지수
|
||||
|
||||
- Redis와 JDBC 중 무엇이 이 상태의 접근 패턴에 맞는가. 요청마다 읽는 값과 가끔 읽는 값이 섞여 있다.
|
||||
- session과 authorized client를 같은 store에 둘지 나눌지.
|
||||
- 암호화 key를 어디에 두고 어떻게 교체하게 되는가. 교체하는 동안 이전 key로 저장된 값은 어떻게 읽는가.
|
||||
- session TTL과 refresh token 수명 중 어느 것을 기준으로 만료를 맞추게 되는가.
|
||||
- 열쇠가 다른 두 store를 logout에서 어떻게 한 번에 지우게 되는가.
|
||||
- sticky session이 durable store의 대안이 되는가 보완이 되는가.
|
||||
|
||||
## 제약
|
||||
|
||||
- authorized client의 열쇠에 session ID가 없어서 session만 공유해도 같은 사용자의 여러 session이 같은 token 항목을 보게 된다.
|
||||
- 커밋된 테스트에 저장소 관련 계약이 없어서 어느 후보를 골라도 지금은 회귀를 잡아 줄 검사가 없다.
|
||||
- 모든 UI 요청이 BFF를 지나기 때문에 저장소 지연이 화면 지연으로 바로 드러나게 된다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 유력 후보 — session과 authorized client를 모두 Redis에 둔다
|
||||
|
||||
Spring Session Redis와 Redis authorized-client repository를 쓰게 되면 만료를 store가 관리해 주고 인스턴스를 늘리기도 쉬워진다.
|
||||
|
||||
Redis를 사용하면 모든 replica가 같은 session과 authorized client를 조회할 수 있다. 인증 경로가 Redis 가용성에 의존하게 되며, access·refresh token 저장 시 암호화 여부와 key 관리 방식도 정해야 한다.
|
||||
|
||||
### 2. session과 authorized client를 모두 JDBC에 둔다
|
||||
|
||||
이미 운영 중인 DB를 쓴다. 백업과 감사 절차가 그 DB에 이미 있다면 그만큼 새로 만들 것이 줄어든다.
|
||||
|
||||
JDBC를 사용하면 기존 관계형 DB 운영 체계를 활용할 수 있지만 인증 요청마다 DB 조회가 발생한다. 만료 데이터 정리와 session 조회 지연도 운영 항목으로 포함해야 한다.
|
||||
|
||||
### 3. 변경이 가장 작은 안 — session만 공유하고 sticky session을 쓴다
|
||||
|
||||
Spring Session만 붙이면 되어서 변경이 가장 적다.
|
||||
|
||||
sticky session만 적용하면 authorized client는 여전히 process-local 상태다. 요청이 다른 인스턴스로 라우팅되거나 해당 인스턴스가 종료될 때 session과 token 상태의 정합을 보장하기 어렵다.
|
||||
|
||||
### 4. session은 Redis, token은 암호화한 JDBC에 둔다
|
||||
|
||||
요청마다 읽는 session은 빠른 저장소에 두고 오래 보관하면서 암호화가 필요한 token은 DB에 두게 되어서 접근 패턴에 맞다.
|
||||
|
||||
session과 authorized client를 서로 다른 저장소에 두면 각각의 TTL과 logout 정리 순서를 맞춰야 하고 운영 대상 저장소도 하나 늘어난다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
후보마다 같은 입력으로 재서 비교한다.
|
||||
|
||||
1. 인스턴스 두 대에서 로그인 유지와 재시작 복구가 되는지 본다.
|
||||
2. 저장소를 직접 열어 refresh token이 평문으로 남는지 확인한다.
|
||||
3. session TTL과 token 만료를 어긋나게 두고 그 순간의 응답과 화면을 기록한다.
|
||||
4. logout 뒤 두 store에 잔여 항목이 없는지 확인한다.
|
||||
5. 저장소를 끊은 상태에서 로그인과 API 호출이 어떤 오류를 내는지 본다.
|
||||
|
||||
암호화 key 교체 절차는 후보를 고른 뒤에 따로 설계한다.
|
||||
Reference in New Issue
Block a user