# Token Management System Guide

완전한 Access Token & Refresh Token 시스템 구현 가이드

## 📋 목차

1. [시스템 개요](#시스템-개요)
2. [토큰 라이프사이클](#토큰-라이프사이클)
3. [API 엔드포인트](#api-엔드포인트)
4. [자동 토큰 갱신](#자동-토큰-갱신)
5. [사용 예시](#사용-예시)
6. [보안 고려사항](#보안-고려사항)

---

## 시스템 개요

### 토큰 구조

```json
{
  "access_token": "214|EU7drdTBYN1fru0MylLXwjJbi2svXcikn5ofvmTI354d09c7",
  "refresh_token": "215|6hAPWcO05jtfSDV9Yz4kLQi3qZDFuycMqrNITOV3c27bd0cb",
  "token_type": "Bearer",
  "expires_in": 7200,
  "expires_at": "2025-11-10 15:49:38"
}
```

### 저장 방식

**HttpOnly 쿠키** (XSS 공격 방지):
- `access_token`: 2시간 만료 (7200초)
- `refresh_token`: 7일 만료 (604800초)

**보안 속성**:
- `HttpOnly`: JavaScript 접근 불가
- `Secure`: HTTPS만 전송
- `SameSite=Strict`: CSRF 공격 방지

---

## 토큰 라이프사이클

### 1. 로그인 (Token 발급)

```
사용자 로그인
    ↓
POST /api/auth/login
    ↓
PHP Backend /api/v1/login
    ↓
access_token + refresh_token 발급
    ↓
HttpOnly 쿠키에 저장
    ↓
대시보드로 이동
```

### 2. 인증된 요청

```
보호된 페이지 접근
    ↓
Middleware 인증 체크
    ↓
access_token 존재?
    ├─ Yes → 접근 허용
    └─ No → refresh_token 확인
          ├─ 있음 → 자동 갱신 시도
          └─ 없음 → 로그인 페이지로
```

### 3. 토큰 갱신

```
access_token 만료 (2시간 후)
    ↓
보호된 API 호출 시도
    ↓
401 Unauthorized 응답
    ↓
POST /api/auth/refresh
    ↓
refresh_token으로 새 토큰 발급
    ↓
새 access_token + refresh_token 쿠키 업데이트
    ↓
원래 API 호출 재시도
    ↓
성공
```

### 4. 로그아웃

```
사용자 로그아웃
    ↓
POST /api/auth/logout
    ↓
PHP Backend /api/v1/logout (토큰 무효화)
    ↓
HttpOnly 쿠키 삭제
    ↓
로그인 페이지로 이동
```

---

## API 엔드포인트

### 1. Login API

**Endpoint**: `POST /api/auth/login`

**Request**:
```typescript
{
  user_id: string;
  user_pwd: string;
}
```

**Response**:
```typescript
{
  message: string;
  user: UserObject;
  tenant: TenantObject | null;
  menus: MenuItem[];
  token_type: "Bearer";
  expires_in: number;
  expires_at: string;
}
```

**쿠키 설정**:
- `access_token` (HttpOnly, 2시간)
- `refresh_token` (HttpOnly, 7일)

---

### 2. Refresh Token API

**Endpoint**: `POST /api/auth/refresh`

**쿠키 필요**: `refresh_token`

**Response** (성공):
```typescript
{
  message: "Token refreshed successfully";
  token_type: "Bearer";
  expires_in: number;
  expires_at: string;
}
```

**Response** (실패):
```typescript
{
  error: "Token refresh failed";
  needsReauth: true;
}
```

**쿠키 업데이트**:
- 새 `access_token` (2시간)
- 새 `refresh_token` (7일)

---

### 3. Auth Check API

**Endpoint**: `GET /api/auth/check`

**기능**:
1. `access_token` 존재 → 200 OK with `authenticated: true`
2. `access_token` 없음 + `refresh_token` 있음 → 자동 갱신 시도
   - 갱신 성공 → 200 OK with `authenticated: true, refreshed: true`
   - 갱신 실패 → 401 Unauthorized
3. 둘 다 없음 → 401 Unauthorized

**Response**:
```typescript
// ✅ 인증 성공 (200)
{
  authenticated: true;
  refreshed?: boolean; // 자동 갱신 여부
}

// ❌ 인증 실패 (401)
{
  error: string; // 'Not authenticated' 또는 'Token refresh failed'
}
```

**참고**:
- 🔵 **Next.js 내부 API** (PHP 백엔드 X)
- 성능 최적화: 로컬 쿠키만 확인하여 빠른 응답
- 로그인/회원가입 페이지에서 이미 로그인된 사용자를 대시보드로 리다이렉트하는 데 사용

---

### 4. Logout API

**Endpoint**: `POST /api/auth/logout`

**기능**:
1. PHP 백엔드에 로그아웃 요청 (토큰 무효화)
2. `access_token`, `refresh_token` 쿠키 삭제

---

## 자동 토큰 갱신

### 1. Middleware에서 자동 갱신

`src/middleware.ts`:
```typescript
// access_token 또는 refresh_token이 있으면 인증됨
const accessToken = request.cookies.get('access_token');
const refreshToken = request.cookies.get('refresh_token');

if ((accessToken && accessToken.value) || (refreshToken && refreshToken.value)) {
  return { isAuthenticated: true, authMode: 'bearer' };
}
```

### 2. Auth Check에서 자동 갱신

`src/app/api/auth/check/route.ts`:
```typescript
// access_token 없고 refresh_token만 있으면 자동 갱신
if (refreshToken && !accessToken) {
  const refreshResponse = await fetch('/api/v1/refresh', {...});
  // 새 토큰을 HttpOnly 쿠키로 설정
}
```

### 3. API Client에서 자동 갱신

`src/lib/api/client.ts`:
```typescript
// withTokenRefresh 헬퍼 함수 사용
const data = await withTokenRefresh(() =>
  apiClient.get('/protected/resource')
);
```

**동작 방식**:
1. API 호출 시도
2. 401 응답 받음
3. `/api/auth/refresh` 호출
4. 성공 시 원래 API 재시도
5. 실패 시 로그인 페이지로 리다이렉트

---

## 사용 예시

### 예시 1: 보호된 페이지에서 API 호출

```typescript
// src/app/[locale]/(protected)/dashboard/page.tsx
import { withTokenRefresh } from '@/lib/api/client';

export default function Dashboard() {
  const fetchData = async () => {
    try {
      // 자동 토큰 갱신 포함
      const data = await withTokenRefresh(() =>
        fetch('/api/protected/data', {
          credentials: 'include' // 쿠키 포함
        })
      );

      console.log('Data fetched:', data);
    } catch (error) {
      console.error('Fetch failed:', error);
    }
  };

  return <div>...</div>;
}
```

### 예시 2: 수동 토큰 갱신

```typescript
// src/lib/auth/token-refresh.ts
import { refreshTokenClient } from '@/lib/auth/token-refresh';

async function handleProtectedAction() {
  try {
    // API 호출
    const response = await fetch('/api/protected/action');

    if (!response.ok) {
      // 401 에러 시 토큰 갱신 시도
      const refreshed = await refreshTokenClient();

      if (refreshed) {
        // 재시도
        return await fetch('/api/protected/action');
      }
    }

    return response;
  } catch (error) {
    console.error('Action failed:', error);
  }
}
```

### 예시 3: Protected Layout

```typescript
// src/app/[locale]/(protected)/layout.tsx
"use client";

import { useAuthGuard } from '@/hooks/useAuthGuard';

export default function ProtectedLayout({ children }) {
  // 자동으로 /api/auth/check 호출
  // access_token 없으면 refresh_token으로 자동 갱신
  useAuthGuard();

  return <>{children}</>;
}
```

---

## 보안 고려사항

### ✅ 구현된 보안 기능

1. **HttpOnly 쿠키**
   - JavaScript에서 토큰 접근 불가
   - XSS 공격으로부터 보호

2. **Secure 플래그**
   - HTTPS에서만 쿠키 전송
   - 중간자 공격 방지

3. **SameSite=Strict**
   - CSRF 공격 방지
   - 크로스 사이트 요청 차단

4. **토큰 만료 시간**
   - Access Token: 2시간 (짧은 수명)
   - Refresh Token: 7일 (긴 수명)

5. **에러 메시지 일반화**
   - 백엔드 상세 에러 노출 방지
   - 정보 유출 차단

### ⚠️ 추가 권장 사항

1. **Token Rotation**
   - Refresh 시 새로운 refresh_token 발급 (현재 구현됨 ✅)

2. **Rate Limiting**
   - 로그인 시도 제한
   - Refresh 요청 제한

3. **IP 검증**
   - 토큰 발급 시 IP 기록
   - 다른 IP에서 사용 시 경고

4. **Device Fingerprinting**
   - 토큰 발급 디바이스 기록
   - 이상 접근 탐지

5. **Logout Blacklist**
   - 로그아웃 된 토큰 블랙리스트 관리
   - 재사용 방지

---

## 트러블슈팅

### 문제 1: 로그인 후 바로 로그아웃됨

**원인**: 쿠키가 설정되지 않음

**해결**:
1. 브라우저 개발자 도구 → Application → Cookies 확인
2. `access_token`, `refresh_token` 존재 확인
3. 없으면 `/api/auth/login` 응답 헤더 확인

### 문제 2: Token refresh 무한 루프

**원인**: Refresh token도 만료됨

**해결**:
1. `/api/auth/refresh` 응답 확인
2. 401 응답 시 로그인 페이지로 리다이렉트
3. `needsReauth: true` 플래그 확인

### 문제 3: CORS 에러

**원인**: 크로스 도메인 요청 시 쿠키 전송 실패

**해결**:
```typescript
fetch('/api/protected', {
  credentials: 'include' // 쿠키 포함
})
```

---

## 참고 파일

- `src/app/api/auth/login/route.ts` - 로그인 API
- `src/app/api/auth/refresh/route.ts` - 토큰 갱신 API
- `src/app/api/auth/check/route.ts` - 인증 체크 API
- `src/app/api/auth/logout/route.ts` - 로그아웃 API
- `src/middleware.ts` - 인증 미들웨어
- `src/lib/auth/token-refresh.ts` - 토큰 갱신 유틸리티
- `src/lib/api/client.ts` - API 클라이언트 (자동 갱신)