diff --git a/troubleshooting/250809_happybell80_OAuth인증시스템.md b/troubleshooting/250809_happybell80_OAuth인증시스템.md new file mode 100644 index 0000000..a2e6fee --- /dev/null +++ b/troubleshooting/250809_happybell80_OAuth인증시스템.md @@ -0,0 +1,219 @@ +# OAuth 인증 시스템 구축 및 CORS 문제 해결 + +작성일: 2025년 8월 9일 +작성자: happybell80 + +## 오후 2시 30분 + +### OAuth 인증 플로우 개선 - JWT 토큰 보안 문제 + +#### 문제 상황 +- 프론트엔드 루트 페이지(/)에 로그인 버튼이 없음 +- OAuth 인증 후 JWT 토큰이 URL 쿼리 파라미터에 노출되는 보안 문제 +- 서버 로그에 토큰이 기록될 위험 + +#### 해결 과정 + +1. **보안 자문 결과** + - JWT 토큰을 URL에 직접 노출하면 안됨 + - 임시 코드 방식 사용 권장 + - Fragment URL (#) 사용으로 서버 로그 노출 방지 + +2. **Redis 기반 임시 인증 코드 시스템 구현** + ```yaml + # docker-compose.yml + services: + redis: + image: redis:alpine + container_name: auth-redis + command: redis-server --appendonly yes + volumes: + - redis_data:/data + ``` + +3. **코드 구현** + - `app/core/redis.py`: Redis 연결 유틸리티 + - `app/routes/auth.py`: `/auth/verify` 엔드포인트 (코드↔토큰 교환) + - `app/providers/gmail.py`: Fragment URL 리다이렉트 + +#### 결과 +- 60초 TTL, 1회용 임시 코드로 보안 강화 +- Fragment URL 사용으로 서버 로그 보안 + +--- + +## 오후 3시 45분 + +### JWT 모듈 누락 문제 + +#### 문제 상황 +``` +토큰 교환 실패 +No module named 'app.core.auth' +``` + +#### 원인 분석 +- `/app/core/auth.py` 파일이 없어서 JWT 토큰 생성 실패 +- `create_access_token()` 함수 누락 + +#### 해결 과정 + +1. **JWT 인증 모듈 생성** + ```python + # app/core/auth.py + from jose import JWTError, jwt # python-jose 사용 + + def create_access_token(data: Dict[str, Any]) -> str: + # JWT 토큰 생성 로직 + + def decode_access_token(token: str) -> Dict[str, Any]: + # JWT 토큰 검증 로직 + ``` + +2. **기존 패키지 활용** + - PyJWT 대신 이미 설치된 `python-jose` 사용 + - 중복 패키지 설치 방지 + +#### 결과 +- JWT 토큰 생성/검증 정상 작동 +- OAuth 콜백 처리 성공 + +--- + +## 오후 4시 10분 + +### 로그인 후 상태 표시 문제 + +#### 문제 상황 +- 로그인 성공 후에도 계속 로그인 버튼 표시 +- 사용자 정보가 UI에 반영되지 않음 + +#### 원인 분석 +1. React 상태 업데이트 타이밍 문제 +2. 비동기 처리로 인한 상태 동기화 실패 +3. URL Fragment 정리 시점과 상태 업데이트 불일치 + +#### 해결 과정 + +1. **프론트엔드 auth-context.tsx 수정** + ```javascript + // 로그인 성공 후 강제 새로고침 + if (data.token) { + localStorage.setItem('auth_token', data.token); + setUser({ ... }); + window.location.reload(); // 상태 동기화 + } + ``` + +2. **Header 컴포넌트에 로그인 버튼 추가** + - useAuth 훅 연동 + - 인증 상태에 따른 버튼 토글 + +#### 결과 +- 로그인 후 페이지 새로고침으로 상태 동기화 +- 사용자 정보 정상 표시 + +--- + +## 오후 4시 20분 + +### CORS Preflight 요청 실패 + +#### 문제 상황 +``` +OPTIONS /auth/verify 400 Bad Request +CORS preflight 요청 실패 +실제 POST 요청이 전송되지 않음 +``` + +#### 원인 분석 +1. auth-server CORS 설정에 `https://ro-being.com` 누락 +2. OPTIONS 요청 핸들러 없음 +3. 개발 서버 포트(5173, 5174) 미등록 + +#### 해결 과정 + +1. **CORS 허용 도메인 추가** + ```python + # app/main.py + allow_origins=[ + "https://ro-being.com", # 추가 + "http://localhost:5173", # 추가 + "http://localhost:5174", # 추가 + # ... 기존 도메인들 + ] + ``` + +2. **OPTIONS 엔드포인트 추가** + ```python + # app/routes/auth.py + @router.options("/auth/verify") + async def verify_auth_code_options(): + return JSONResponse(content={}, status_code=200) + ``` + +3. **Request 파싱 수정** + ```python + # JSON body 파싱 + body = await request.json() + code = body.get("code") + ``` + +#### 결과 +- CORS preflight 요청 성공 +- 프론트엔드에서 auth-server API 호출 정상 +- 로그인 플로우 완전 정상화 + +--- + +## 교훈 및 베스트 프랙티스 + +### 1. **보안 우선 설계** +- JWT 토큰은 절대 URL에 노출하지 않기 +- 임시 코드 + Redis 조합으로 안전한 토큰 교환 +- Fragment URL 사용으로 서버 로그 보안 + +### 2. **CORS 설정 체크리스트** +- 모든 프론트엔드 도메인 등록 필수 +- OPTIONS 요청 핸들러 구현 +- 개발/프로덕션 환경 모두 고려 + +### 3. **디버깅 팁** +- 브라우저 개발자 도구 Network 탭 확인 +- Console 로그로 토큰 저장 확인 +- CORS 에러는 서버 로그가 아닌 브라우저에서 확인 + +### 4. **상태 관리** +- React 상태 업데이트는 비동기 +- 중요한 상태 변경 후 새로고침 고려 +- localStorage와 React 상태 동기화 주의 + +### 5. **모듈 재사용** +- 기존 패키지 확인 후 중복 설치 방지 +- python-jose vs PyJWT 선택 일관성 +- requirements.txt 정기적 점검 + +--- + +## 최종 시스템 구성 + +### 인증 플로우 +1. 사용자 → 로그인 버튼 클릭 +2. Google OAuth 인증 +3. auth-server → Redis에 임시 코드 저장 (60초) +4. Fragment URL로 리다이렉트 (`#auth=코드`) +5. 프론트엔드 → `/auth/verify`로 코드 전송 +6. auth-server → JWT 토큰 반환 +7. 프론트엔드 → localStorage 저장 & 새로고침 + +### 핵심 컴포넌트 +- **auth-server**: OAuth 프로바이더, JWT 발급 +- **Redis**: 임시 코드 저장 (TTL 60초) +- **frontend-customer**: 로그인 UI, 토큰 관리 +- **nginx**: 리버스 프록시, SSL 처리 + +### 보안 특징 +- JWT 토큰 URL 노출 방지 +- 1회용 임시 코드 (atomic getdel) +- CORS 화이트리스트 방식 +- Fragment URL로 서버 로그 보안 \ No newline at end of file