103 lines
9.8 KiB
Markdown
103 lines
9.8 KiB
Markdown
---
|
|
title: Curity — The Backend-for-Frontend (BFF) Pattern for SPAs
|
|
source_type: company-tech-blog
|
|
status: raw
|
|
confidence: medium
|
|
url: https://curity.io/resources/learn/the-bff-pattern/
|
|
archive_url:
|
|
tags: [keycloak-patterns, p2a-spa-resource-server, bff, spa, token-storage, oauth-agent, company-tech-blog, curity]
|
|
related_projects: [keycloak-patterns]
|
|
related_branches: [feature-keycloak-patterns, feature-keycloak-bff-vs-spa-direct, feature-keycloak-internal-spa-direct-no-google]
|
|
created: 2026-05-25
|
|
last_reviewed: 2026-05-27
|
|
---
|
|
|
|
# Curity — The Backend-for-Frontend (BFF) Pattern for SPAs
|
|
|
|
> Layer: `raw/company-tech-blogs/` — Curity AB (스웨덴 OAuth/OIDC 전문 vendor) 의 learn / article 콘텐츠. Curity 의 vendor product 자체 명세가 아닌 **article/blog style** 이므로 **company-tech-blog / 사례 + 관점** 으로 취급. 공식 best practice 가 아닌 권고.
|
|
> P2A 는 SPA 가 토큰을 직접 보유하는 흐름. BFF 는 그 대안으로 토큰을 백엔드 (BFF) 가 보관하고 SPA 에는 httpOnly session cookie 만 발급. P2A 의 trade-off 비교 근거.
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-keycloak-patterns]] | keycloak-patterns root — SPA 의 token storage 결정에서 BFF 대안의 존재와 trade-off 정리 |
|
|
| [[raw/branch-notes/feature-keycloak-bff-vs-spa-direct]] | "SPA Direct vs BFF" 결정의 BFF 측 권고 근거 — 토큰을 브라우저에서 제거하는 보안 motivation |
|
|
| [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] | P2A SPA Direct 채택 결정 시 "BFF 는 학습 목적상 후순위" 라는 trade-off 의 비교 baseline |
|
|
|
|
## 컨텍스트 / 왜 저장했는지
|
|
|
|
P2A 는 SPA 가 토큰을 직접 보유하는 흐름. BFF 는 그 대안으로 토큰을 백엔드 (BFF) 가 보관하고 SPA 에는 httpOnly session cookie 만 발급. P2A 의 trade-off 를 비교하기 위한 근거. "SPA Direct vs BFF" 결정 시 인용. Curity 가 vendor 이므로 본 자료는 OAuth 2.1 draft 의 BFF 권고와는 별도로 vendor 관점의 권고로 취급.
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://curity.io/resources/learn/the-bff-pattern/ — **2026-05-27 fetch 성공**
|
|
- 저자 / 조직: Curity AB (스웨덴 OAuth/OIDC 전문 vendor) — 회사 learn 자료
|
|
- 발행일: 미상 (rolling docs)
|
|
- 마지막 확인일: 2026-05-27
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> [§Why Tokens Shouldn't Be in the Browser] "The only way to protect tokens from being accessed by any malicious code is to keep them away from the browser."
|
|
|
|
> [§XSS / Malicious Code Risks] "Any malicious code that manages to run in the context of the SPA will potentially be able to read the access and refresh tokens."
|
|
|
|
> [§OAuth Agent Role] "All communication from the SPA to the authorization server goes via a backend `OAuth Agent` component, and tokens will not reach the SPA at all."
|
|
|
|
> [§HTTP-Only Session Cookies] "The OAuth Agent then issues HTTP-only session cookies to the SPA. The security level is on par with a website backend."
|
|
|
|
> [§SPA Developer Control Over UX] "The SPA developer is also in full control of all usability-related behaviors and can handle redirects, token refresh and session expiry using JSON responses."
|
|
|
|
> [§Refresh Token / Session Expiry] "If the attacker manages to extract a refresh token in this way, they will be able to access the victim's data for as long as that refresh token remains valid."
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| CURITY-BFF-C1 | 브라우저 내 token 을 악성 코드 (XSS 등) 로부터 보호하는 **유일한 방법** 은 token 을 브라우저 밖에 두는 것 | [§Why Tokens Shouldn't Be in the Browser] "The only way to protect tokens from being accessed by any malicious code is to keep them away from the browser." | `company-case-study` | SPA 의 token 보관 위치 결정 | Curity 의 vendor 권고. "유일한 방법" 은 vendor 의 강한 주장이며 OAuth 표준의 공식 표현은 아님 — [[raw/official-docs/oauth-v2-1-draft-ietf]] 와 별도 |
|
|
| CURITY-BFF-C2 | SPA 컨텍스트에서 실행되는 악성 코드는 access token 과 refresh token 을 읽을 수 있는 잠재력이 있음 | [§XSS / Malicious Code Risks] "Any malicious code that manages to run in the context of the SPA will potentially be able to read the access and refresh tokens." | `company-case-study` | XSS 위협 모델이 유의한 SPA | XSS 가 항상 발생한다는 뜻 아님 — CSP / 입력 sanitization 으로 완화 가능. 본 인용은 위협의 잠재성만 |
|
|
| CURITY-BFF-C3 | BFF 패턴에서 SPA 와 authorization server (예: Keycloak) 간 모든 통신은 backend `OAuth Agent` 를 경유하며, token 은 SPA 에 도달하지 않음 | [§OAuth Agent Role] "All communication from the SPA to the authorization server goes via a backend `OAuth Agent` component, and tokens will not reach the SPA at all." | `company-case-study` | Curity 의 BFF / Token Handler 패턴 구현 | "OAuth Agent" 가 Curity 의 product 명명. 다른 vendor (Auth0, IdentityServer) 의 BFF 도 동일 구조라는 뜻 아님 — vendor-specific |
|
|
| CURITY-BFF-C4 | OAuth Agent 는 SPA 에 HTTP-only session cookie 를 발급 — 이는 server-side rendered 웹 백엔드와 동등한 보안 수준 | [§HTTP-Only Session Cookies] "The OAuth Agent then issues HTTP-only session cookies to the SPA. The security level is on par with a website backend." | `company-case-study` | BFF 가 session cookie 를 발급하는 구현 | "동등한 보안 수준" 의 정량 기준 없음. CSRF / cookie scope / SameSite 설정 등 추가 보안 통제는 별도 필요 |
|
|
| CURITY-BFF-C5 | BFF 패턴에서도 SPA 개발자는 redirect / token refresh / session expiry 동작을 JSON response 로 제어 가능 — UX 자유도 유지 | [§SPA Developer Control Over UX] "The SPA developer is also in full control of all usability-related behaviors and can handle redirects, token refresh and session expiry using JSON responses." | `company-case-study` | Curity 의 BFF 구현이 SPA 에 JSON API 를 노출하는 경우 | 모든 BFF 구현이 JSON API 를 노출한다는 뜻 아님 — 일부는 server-side redirect 만 (vendor 마다 다름) |
|
|
| CURITY-BFF-C6 | refresh token 이 탈취되면, 공격자는 refresh token 의 유효 기간 동안 victim 의 데이터에 접근 가능 — 이것이 SPA Direct 의 핵심 위험 | [§Refresh Token / Session Expiry] "If the attacker manages to extract a refresh token in this way, they will be able to access the victim's data for as long as that refresh token remains valid." | `company-case-study` | SPA Direct 에서 refresh token 을 브라우저에 저장하는 경우 | refresh token rotation / DPoP / token binding 같은 mitigation 으로 위험 완화 가능 — 본 인용은 mitigation 미언급 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- **이 자료가 직접 증명하는 것**:
|
|
- `C1`~`C6`: Curity 가 BFF 패턴을 권고하는 motivation (token 격리 / XSS 위험 / OAuth Agent 역할 / cookie 발급 / UX 자유도 / refresh token 탈취 위험)
|
|
- **이 자료가 증명하지 않는 것**:
|
|
- "BFF 가 OAuth 표준의 공식 best practice" — Curity 는 vendor 이며 본 자료는 article. 공식 권고는 [[raw/official-docs/oauth-v2-1-draft-ietf]] 같은 표준 문서로 별도 확인 (CLAUDE.md §5 "company-tech-blog 은 공식 best practice 로 취급 금지")
|
|
- BFF 가 모든 SPA 시나리오에 적용 가능 — public client / native app / IoT 는 trade-off 다름
|
|
- OAuth Agent 의 구체 구현 (어떤 framework / language / token store) — vendor-specific
|
|
- SPA Direct 가 안전하지 않다는 절대적 주장 — refresh token rotation / DPoP / short TTL access token 으로 완화 가능
|
|
- BFF 도입 시 backend stateful (session store) 의 운영 비용
|
|
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
|
- keycloak-patterns 의 P2A (SPA Direct) 가 학습 목적상 채택된 것이며, 운영 환경에서는 BFF 가 권고된다는 결정의 출처 — 본 raw + OAuth 2.1 draft (official-doc) 의 결합 인용 필요
|
|
- BFF 로 전환 시 Keycloak 의 client type (`confidential` vs `public`) 변경 절차
|
|
- session store 의 backend (Redis / DB) 선택과 SLO 영향
|
|
|
|
## 메모 / Notes (내 해석, 미검증)
|
|
|
|
- BFF 구성요소:
|
|
- **OAuth Agent**: 백엔드 component. Keycloak과 Authorization Code + PKCE 수행. token store 보유.
|
|
- **API Gateway / BFF API**: SPA가 호출하는 endpoint. httpOnly session cookie로 사용자 식별.
|
|
- **SPA**: 토큰 없음. session cookie + (필요 시) CSRF token.
|
|
- P2A SPA Direct와의 비교:
|
|
- 보안: BFF 우위 (브라우저에 토큰 없음).
|
|
- 운영: SPA Direct 우위 (백엔드 stateless, session store 불필요).
|
|
- 다중 클라이언트: SPA Direct가 단순 (모바일 / IoT가 같은 JWT 사용). BFF는 클라이언트마다 별도 OAuth client.
|
|
- OAuth 2.1 draft도 SPA가 credentials 사용 시 BFF 권고 → [[raw/official-docs/oauth-v2-1-draft-ietf]] 로 corroborate 필요.
|
|
- 본 branch (P2A) 는 학습 목적으로 SPA Direct 채택 — canonical OIDC + PKCE 흐름을 직접 이해하는 것이 우선. BFF 는 비교 / 발전 방향으로만 기록.
|
|
|
|
## Related / 관련
|
|
|
|
- 같은 주제 다른 raw:
|
|
- [[raw/official-docs/oauth-v2-1-draft-ietf]] (OAuth 2.1 draft — SPA 권고의 공식 표준 측 근거)
|
|
- 인용하는 branch / project:
|
|
- [[raw/branch-notes/feature-keycloak-patterns]] (root)
|
|
- [[raw/branch-notes/feature-keycloak-bff-vs-spa-direct]] (BFF 권고의 직접 결정 노트)
|
|
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] (P2A SPA Direct 채택 결정의 비교 baseline)
|
|
- 인용하는 project:
|
|
- [[raw/project-notes/keycloak-patterns-overview]]
|
|
- 인용한 wiki 요약: (미작성)
|