- generic [ref=f5e3]: - link "본문으로 건너뛰기" [ref=f5e4] [cursor=pointer]: - /url: "#main-content" - banner [ref=f5e5]: - generic [ref=f5e6]: - link "TechLog Studio" [ref=f5e7] [cursor=pointer]: - /url: /studio - text: TechLog - generic [ref=f5e8]: Studio - navigation "Studio 주 탐색" [ref=f5e10]: - link "작업본" [ref=f5e11] [cursor=pointer]: - /url: /studio/documents - link "게시 기록" [ref=f5e12] [cursor=pointer]: - /url: /studio/publications - link "새 문서" [ref=f5e13] [cursor=pointer]: - /url: /studio/documents/new - link "주제·프로젝트" [ref=f5e14] [cursor=pointer]: - /url: /studio/taxonomy - link "릴리즈" [ref=f5e15] [cursor=pointer]: - /url: /studio/releases - link "공개 사이트 보기" [ref=f5e16] [cursor=pointer]: - /url: / - button "로그아웃" [ref=f5e17] - main [ref=f5e18]: - generic [ref=f5e19]: - generic [ref=f5e20]: - region [ref=f5e21]: - generic [ref=f5e22]: - paragraph [ref=f5e23]: PROJECT_DECISION · VERSION 12 - heading "문서 편집" [level=1] [ref=f5e24] - paragraph [ref=f5e25]: 인증 구조를 보안 성숙도 단계로 취급하지 않는다 - region [ref=f5e26]: - generic [ref=f5e27]: - paragraph [ref=f5e28]: DOCUMENT - heading "기본 정보" [level=2] [ref=f5e29] - generic [ref=f5e30]: - generic [ref=f5e31]: - generic [ref=f5e32]: 제목 - textbox "제목" [ref=f5e33]: 인증 구조를 보안 성숙도 단계로 취급하지 않는다 - generic [ref=f5e34]: - generic [ref=f5e35]: slug - textbox "slug" [ref=f5e36]: - /placeholder: 비우면 제목에서 만듭니다 (영문 소문자·숫자·하이픈) - text: patterns-are-not-a-maturity-ladder - generic [ref=f5e37]: - generic [ref=f5e38]: 요약 - textbox "요약" [ref=f5e39]: SPA, Mediator, BFF, Forward-Auth는 credential을 처리하는 주체와 API 호출 경로가 서로 다르다. 번호나 브라우저 token 노출 여부를 보안 등급으로 사용하지 않고 각각 별도의 아키텍처 패턴으로 취급한다. - generic [ref=f5e40]: - generic [ref=f5e41]: Topic - combobox "Topic" [ref=f5e42]: - option "선택하지 않음" - option "OAuth/OIDC 인증 경계" [selected] - generic [ref=f5e43]: - generic [ref=f5e44]: Project - combobox "Project" [ref=f5e45]: - option "미지정" - option "Backend Clean Architecture" - option "KeyCloak Patterns" [selected] - option "Liner N + 1문제" - group "근거 기록" [ref=f5e46]: - generic [ref=f5e48]: - generic [ref=f5e49]: - generic [ref=f5e50]: 근거 1 대상 - combobox "근거 1 대상" [ref=f5e51]: - option "대상 선택" - option "인증 구조를 보안 성숙도 단계로 취급하지 않는다" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "Browser에서 OAuth Token을 제거해야 하는 경우 BFF가 Token을 소유한다" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [disabled] - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [disabled] - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP Federation을 별도의 인증 구조로 세지 않는다" - option "외부 IdP Federation과 Application 인증 경계" - option "OAuth/OIDC 인증 패턴 선택 기준" [disabled] - option "OAuth Token과 Application Session을 구분하는 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [disabled] - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [selected] - generic [ref=f5e52]: - generic [ref=f5e53]: 근거 1 이유 - textbox "근거 1 이유" [ref=f5e54]: 브라우저가 code 교환, token 보관, API 호출을 직접 수행한다. - generic [ref=f5e55]: - button "위로" [disabled] [ref=f5e56] - button "아래로" [ref=f5e57] - button "삭제" [ref=f5e58] - generic [ref=f5e59]: - generic [ref=f5e60]: - generic [ref=f5e61]: 근거 2 대상 - combobox "근거 2 대상" [ref=f5e62]: - option "대상 선택" - option "인증 구조를 보안 성숙도 단계로 취급하지 않는다" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "Browser에서 OAuth Token을 제거해야 하는 경우 BFF가 Token을 소유한다" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [disabled] - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [disabled] - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP Federation을 별도의 인증 구조로 세지 않는다" - option "외부 IdP Federation과 Application 인증 경계" - option "OAuth/OIDC 인증 패턴 선택 기준" [disabled] - option "OAuth Token과 Application Session을 구분하는 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [selected] - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [disabled] - generic [ref=f5e63]: - generic [ref=f5e64]: 근거 2 이유 - textbox "근거 2 이유" [ref=f5e65]: mediator가 code 교환과 refresh token 보관을 담당하고 브라우저가 access token으로 API를 직접 호출한다. - generic [ref=f5e66]: - button "위로" [ref=f5e67] - button "아래로" [ref=f5e68] - button "삭제" [ref=f5e69] - generic [ref=f5e70]: - generic [ref=f5e71]: - generic [ref=f5e72]: 근거 3 대상 - combobox "근거 3 대상" [ref=f5e73]: - option "대상 선택" - option "인증 구조를 보안 성숙도 단계로 취급하지 않는다" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "Browser에서 OAuth Token을 제거해야 하는 경우 BFF가 Token을 소유한다" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [selected] - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [disabled] - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP Federation을 별도의 인증 구조로 세지 않는다" - option "외부 IdP Federation과 Application 인증 경계" - option "OAuth/OIDC 인증 패턴 선택 기준" [disabled] - option "OAuth Token과 Application Session을 구분하는 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [disabled] - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [disabled] - generic [ref=f5e74]: - generic [ref=f5e75]: 근거 3 이유 - textbox "근거 3 이유" [ref=f5e76]: BFF가 token과 session을 server-side에서 관리하고 Resource Server를 호출한다. - generic [ref=f5e77]: - button "위로" [ref=f5e78] - button "아래로" [ref=f5e79] - button "삭제" [ref=f5e80] - generic [ref=f5e81]: - generic [ref=f5e82]: - generic [ref=f5e83]: 근거 4 대상 - combobox "근거 4 대상" [ref=f5e84]: - option "대상 선택" - option "인증 구조를 보안 성숙도 단계로 취급하지 않는다" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "Browser에서 OAuth Token을 제거해야 하는 경우 BFF가 Token을 소유한다" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [disabled] - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [selected] - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP Federation을 별도의 인증 구조로 세지 않는다" - option "외부 IdP Federation과 Application 인증 경계" - option "OAuth/OIDC 인증 패턴 선택 기준" [disabled] - option "OAuth Token과 Application Session을 구분하는 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [disabled] - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [disabled] - generic [ref=f5e85]: - generic [ref=f5e86]: 근거 4 이유 - textbox "근거 4 이유" [ref=f5e87]: oauth2-proxy가 인증을 처리하고 upstream에는 identity header를 전달한다. - generic [ref=f5e88]: - button "위로" [ref=f5e89] - button "아래로" [ref=f5e90] - button "삭제" [ref=f5e91] - generic [ref=f5e92]: - generic [ref=f5e93]: - generic [ref=f5e94]: 근거 5 대상 - combobox "근거 5 대상" [ref=f5e95]: - option "대상 선택" - option "인증 구조를 보안 성숙도 단계로 취급하지 않는다" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "Browser에서 OAuth Token을 제거해야 하는 경우 BFF가 Token을 소유한다" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [disabled] - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [disabled] - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP Federation을 별도의 인증 구조로 세지 않는다" - option "외부 IdP Federation과 Application 인증 경계" - option "OAuth/OIDC 인증 패턴 선택 기준" [selected] - option "OAuth Token과 Application Session을 구분하는 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [disabled] - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [disabled] - generic [ref=f5e96]: - generic [ref=f5e97]: 근거 5 이유 - textbox "근거 5 이유" [ref=f5e98]: 이 결정을 적용하는 선택 기준이다. - generic [ref=f5e99]: - button "위로" [ref=f5e100] - button "아래로" [disabled] [ref=f5e101] - button "삭제" [ref=f5e102] - button "근거 추가" [ref=f5e103] - region [ref=f5e104]: - generic [ref=f5e105]: - paragraph [ref=f5e106]: PROJECT DECISION - heading "프로젝트 결정" [level=2] [ref=f5e107] - generic [ref=f5e108]: - generic [ref=f5e109]: - generic [ref=f5e110]: 결정 상태 - combobox "결정 상태" [ref=f5e111]: - option "아직 정하지 않음" - option "PROPOSED" - option "ADOPTED" [selected] - generic [ref=f5e112]: - generic [ref=f5e113]: 결정일 - textbox "결정일" [ref=f5e114]: 2026-08-24 - generic [ref=f5e115]: - generic [ref=f5e116]: 결정문 - textbox "결정문" [ref=f5e117]: SPA에서 Mediator, BFF, OAuth2-Proxy로 가는 순서를 낮은 보안에서 높은 보안으로 가는 단계로 모델링하지 않는다. 네 구조는 credential과 인증 상태를 처리하는 주체가 서로 다른 별개의 아키텍처 패턴으로 취급한다. - generic [ref=f5e118]: - generic [ref=f5e119]: 판단 이유 - textbox "판단 이유" [ref=f5e120]: BFF는 브라우저 token을 없애지만 server session과 CSRF, 공유 저장소를 만든다. Forward-Auth는 애플리케이션의 token custody를 줄이지만 edge 헤더 신뢰와 network 경계를 만든다. 뒤 구조가 앞 구조의 문제를 없애는 것이 아니라 다른 곳에 다른 요구를 만든다. 네 구조의 차이는 code를 교환하는 주체, token 저장 방식, API 호출 주체, 보호 자원이 신뢰하는 credential에서 확인됐다. 이 차이를 보안 성숙도 순서로 환산하지 않는다. 성숙도 모델로 두면 「일단 제일 뒤 구조로 가자」는 판단이 나온다. backend 직접 경로를 닫을 수 없는 환경에서 edge에 인증을 맡기면 upstream이 헤더 하나로 사용자를 판단하는데 그 헤더를 누구나 만들어 보낼 수 있다. 그런 환경에서는 브라우저가 token을 직접 들고 서명을 검증받는 구조가 낫다. - group "영향" [ref=f5e121]: - generic [ref=f5e123]: - generic [ref=f5e124]: - generic [ref=f5e125]: 영향 1 - textbox "영향 1" [ref=f5e126]: 패턴을 비교할 때는 적용 조건과 운영해야 할 상태, 신뢰 경계, 장애 지점을 함께 적는다. 브라우저 token 노출 여부 하나만으로 순서를 매기지 않는다. - generic [ref=f5e127]: - button "위로" [disabled] [ref=f5e128] - button "아래로" [ref=f5e129] - button "삭제" [ref=f5e130] - generic [ref=f5e131]: - generic [ref=f5e132]: - generic [ref=f5e133]: 영향 2 - textbox "영향 2" [ref=f5e134]: 구조를 고를 때 번호가 아니라 code 교환·token 보관·API 호출의 배치를 먼저 답한다. 뒤 구조에서 앞 구조로 되돌아가는 선택도 후퇴가 아니라 credential 계약의 변경으로 적는다. - generic [ref=f5e135]: - button "위로" [ref=f5e136] - button "아래로" [ref=f5e137] - button "삭제" [ref=f5e138] - generic [ref=f5e139]: - generic [ref=f5e140]: - generic [ref=f5e141]: 영향 3 - textbox "영향 3" [ref=f5e142]: 구조 이름만으로 운영 속성을 추정하지 않는다. 공유 저장소와 장애 복구, secret 교체는 매번 따로 확인한다. - generic [ref=f5e143]: - button "위로" [ref=f5e144] - button "아래로" [disabled] [ref=f5e145] - button "삭제" [ref=f5e146] - button "영향 추가" [ref=f5e147] - region [ref=f5e148]: - generic [ref=f5e149]: - paragraph [ref=f5e150]: LIVE - heading "즉시 미리보기" [level=2] [ref=f5e151] - generic [ref=f5e154]: - navigation "문서 경로" [ref=f5e155]: - link "Project" [ref=f5e156] [cursor=pointer]: - /url: /projects - generic [ref=f5e157]: / - link "KeyCloak Patterns" [ref=f5e158] [cursor=pointer]: - /url: /projects/keycloak-patterns - generic [ref=f5e159]: / - link "Decision" [ref=f5e160] [cursor=pointer]: - /url: /projects/keycloak-patterns/decisions - list [ref=f5e161]: - listitem [ref=f5e162]: - article [ref=f5e163]: - generic [ref=f5e164]: - generic [ref=f5e165]: - generic [ref=f5e166]: ADOPTED - time [ref=f5e167]: 2026.08.24 - heading "인증 구조를 보안 성숙도 단계로 취급하지 않는다" [level=2] [ref=f5e168] - paragraph [ref=f5e169]: SPA에서 Mediator, BFF, OAuth2-Proxy로 가는 순서를 낮은 보안에서 높은 보안으로 가는 단계로 모델링하지 않는다.네 구조는 credential과 인증 상태를 처리하는 주체가 서로 다른 별개의 아키텍처 패턴으로 취급한다. - generic [ref=f5e170]: - heading "판단 이유" [level=3] [ref=f5e171] - paragraph [ref=f5e172]: BFF는 브라우저 token을 없애지만 server session과 CSRF, 공유 저장소를 만든다. Forward-Auth는 애플리케이션의 token custody를 줄이지만 edge 헤더 신뢰와 network 경계를 만든다. 뒤 구조가 앞 구조의 문제를 없애는 것이 아니라 다른 곳에 다른 요구를 만든다.네 구조의 차이는 code를 교환하는 주체, token 저장 방식, API 호출 주체, 보호 자원이 신뢰하는 credential에서 확인됐다. 이 차이를 보안 성숙도 순서로 환산하지 않는다.성숙도 모델로 두면 「일단 제일 뒤 구조로 가자」는 판단이 나온다. backend 직접 경로를 닫을 수 없는 환경에서 edge에 인증을 맡기면 upstream이 헤더 하나로 사용자를 판단하는데 그 헤더를 누구나 만들어 보낼 수 있다. 그런 환경에서는 브라우저가 token을 직접 들고 서명을 검증받는 구조가 낫다. - generic [ref=f5e173]: - heading "영향" [level=3] [ref=f5e174] - list [ref=f5e175]: - listitem [ref=f5e176]: 패턴을 비교할 때는 적용 조건과 운영해야 할 상태, 신뢰 경계, 장애 지점을 함께 적는다. 브라우저 token 노출 여부 하나만으로 순서를 매기지 않는다. - listitem [ref=f5e177]: 구조를 고를 때 번호가 아니라 code 교환·token 보관·API 호출의 배치를 먼저 답한다. 뒤 구조에서 앞 구조로 되돌아가는 선택도 후퇴가 아니라 credential 계약의 변경으로 적는다. - listitem [ref=f5e178]: 구조 이름만으로 운영 속성을 추정하지 않는다. 공유 저장소와 장애 복구, secret 교체는 매번 따로 확인한다. - generic [ref=f5e179]: - heading "근거 기록" [level=3] [ref=f5e180] - list [ref=f5e181]: - listitem [ref=f5e182]: - link "SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계" [ref=f5e183] [cursor=pointer]: - /url: /cases/spa-browser-credential-boundary - listitem [ref=f5e184]: - link "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" [ref=f5e185] [cursor=pointer]: - /url: /cases/split-custody-access-token - listitem [ref=f5e186]: - link "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" [ref=f5e187] [cursor=pointer]: - /url: /cases/bff-session-csrf-responsibility - listitem [ref=f5e188]: - link "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" [ref=f5e189] [cursor=pointer]: - /url: /cases/identity-header-trust - complementary [ref=f5e190]: - heading "작업 상태" [level=2] [ref=f5e191] - status "편집 상태" [ref=f5e192]: 저장됨 - generic [ref=f5e193]: - generic [ref=f5e194]: - term [ref=f5e195]: 저장 버전 - definition [ref=f5e196]: "12" - generic [ref=f5e197]: - term [ref=f5e198]: 종류 - definition [ref=f5e199]: Decision - paragraph [ref=f5e200]: 불완전한 초안도 저장할 수 있습니다. Ctrl+S 로도 저장합니다. 게시를 누르면 채워야 할 칸을 그 자리에 표시합니다. - generic [ref=f5e201]: - button "저장" [disabled] [ref=f5e202] - button "게시" [ref=f5e203] - paragraph [ref=f5e204]: 버전 12으로 저장했습니다.