Files
frontend/docs/02.multi-domain-jwt.md
T

155 lines
4.4 KiB
Markdown

# Multi-Domain JWT 토큰 공유 분석
## 현재 구조 진단
### 도메인 구성
| 앱 | 도메인 | 포트(로컬) |
|----|--------|-----------|
| cs | `cs.onasys.co.kr` | 3000 |
| as | `as.onasys.co.kr` | 3003 |
| backend | 공통 1개 | 8000 |
### 핵심 문제
브라우저 쿠키는 **Same-Origin Policy** 적용: `cs.onasys.co.kr`에서 설정한 쿠키는 `as.onasys.co.kr`에서 **읽을 수 없음**.
현재 두 앱 모두 `NEXT_PUBLIC_COOKIE_DOMAIN` 환경변수가 **설정되지 않음** → 쿠키 `domain` 속성 없음 → 각 앱이 자기 도메인에만 쿠키 저장.
### 코드 흐름 분석
```
authConfig.cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN (현재 undefined)
↓
token-manager.ts: getCookieDomain() → undefined 반환
↓
setCookie(key, value, { domain: undefined, ... }) ← domain 없이 저장
↓
결과: cs.onasys.co.kr 쿠키 ≠ as.onasys.co.kr 쿠키 (공유 불가)
```
---
## 해결 방법: Parent Domain Cookie
### 원리
쿠키 `Domain` 속성을 **부모 도메인**으로 설정하면 모든 서브도메인이 공유:
```
Domain=.onasys.co.kr
→ cs.onasys.co.kr ✅ 읽기/쓰기 가능
→ as.onasys.co.kr ✅ 읽기/쓰기 가능
```
---
## 구현 방법
### Step 1: 환경변수 추가
**apps/cs/.env.production** (또는 `.env.local` 배포용)
```env
NEXT_PUBLIC_COOKIE_DOMAIN=.onasys.co.kr
```
**apps/as/.env.production**
```env
NEXT_PUBLIC_COOKIE_DOMAIN=.onasys.co.kr
```
> [!IMPORTANT]
> 반드시 앞에 `.` (점)을 붙여야 서브도메인 전체에 적용됩니다.
### Step 2: 코드 변경 불필요
`token-manager.ts`의 `getCookieDomain()`이 이미 `config.cookieDomain`을 사용하고 있으므로, 환경변수만 설정하면 됩니다.
```ts
// packages/common/auth/token-manager.ts (현재 코드)
const getCookieDomain = (): string | undefined => {
if (typeof window === "undefined") return undefined;
if (config.cookieDomain) return config.cookieDomain; // ← 이미 준비됨
return undefined;
};
```
### Step 3: 백엔드 CORS 확인
백엔드가 `credentials: true`와 함께 올바른 `Access-Control-Allow-Origin`을 반환하는지 확인:
```
Access-Control-Allow-Origin: https://cs.onasys.co.kr (또는 as.onasys.co.kr)
Access-Control-Allow-Credentials: true
```
> [!WARNING]
> `Access-Control-Allow-Origin: *` 와 `credentials: true`는 동시에 사용 불가.
> 명시적 도메인 또는 동적 오리진 허용 필요.
---
## SameSite 주의사항
현재 토큰 매니저 설정:
```ts
setCookie(key, value, {
secure: isHttps,
sameSite: "lax", // ← 서브도메인 간 이동 시 쿠키 전송됨 (OK)
domain,
path: "/",
});
```
- `sameSite: "lax"` → 서브도메인 간 네비게이션에서 쿠키 전송 **가능** ✅
- `sameSite: "strict"` → 다른 도메인에서의 요청에 쿠키 미전송 ❌
- `sameSite: "none"` + `secure: true` → 크로스사이트 iframe 등에서도 전송 (필요 시)
배포 환경 HTTPS 사용 시 `secure: true` 자동 적용되므로 문제 없음.
---
## 세션 공유 흐름 (적용 후)
```
사용자가 cs.onasys.co.kr 에서 로그인
↓
setCookie("access-token", jwt, { domain: ".onasys.co.kr" })
↓
브라우저: .onasys.co.kr 쿠키 저장
↓
사용자가 as.onasys.co.kr 으로 이동
↓
브라우저가 자동으로 ".onasys.co.kr" 쿠키 전송
↓
as 앱의 미들웨어: req.cookies.get("access-token") ✅ 정상 읽힘
↓
백엔드 API 호출 시 Authorization 헤더에 토큰 포함
```
---
## 추가 고려사항
### 로그아웃 처리
한 앱에서 로그아웃 시 **도메인 지정 쿠키 삭제** 필요:
```ts
// removeToken 에서 domain 이 자동 적용됨 (현재 코드 OK)
removeCookie(key, { domain: ".onasys.co.kr", path: "/" });
```
### 토큰 갱신 (Refresh Token)
`refresh-token` 쿠키도 같은 `domain` 설정 적용 → 두 앱 모두 silent refresh 가능.
### 로컬 개발 환경
로컬에서는 `localhost`가 서브도메인을 지원하지 않으므로 별도 처리 불필요 (`.env.local`에는 설정하지 않음).
---
## 최소 변경 요약
| 위치 | 변경 내용 |
|------|----------|
| `apps/cs/.env.production` | `NEXT_PUBLIC_COOKIE_DOMAIN=.onasys.co.kr` 추가 |
| `apps/as/.env.production` | `NEXT_PUBLIC_COOKIE_DOMAIN=.onasys.co.kr` 추가 |
| 소스 코드 | **변경 없음** (이미 cookieDomain 지원 구현됨) |
| 백엔드 | CORS `Access-Control-Allow-Origin` 명시적 도메인 설정 확인 |