- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동 - book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서) - 빈 폴더 제거 (journey/assets/*)
219 lines
5.6 KiB
Markdown
219 lines
5.6 KiB
Markdown
# 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로 서버 로그 보안 |