From da2f380739eabed98c6c03362532fabe73c9b8b6 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Thu, 21 Aug 2025 12:02:45 +0900 Subject: [PATCH] =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B0=B1=EC=8B=A0=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 250821_gmail_token_auto_refresh.md | 227 +++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 250821_gmail_token_auto_refresh.md diff --git a/250821_gmail_token_auto_refresh.md b/250821_gmail_token_auto_refresh.md new file mode 100644 index 0000000..8c83b5c --- /dev/null +++ b/250821_gmail_token_auto_refresh.md @@ -0,0 +1,227 @@ +# Gmail 토큰 자동 갱신 기능 구현 + +## 작성일: 2025-08-21 +## 작성자: Claude (with heejae) + +--- + +## 1. 개요 + +Gmail OAuth 토큰의 자동 갱신 기능을 auth-server에 구현하여, 만료된 access_token을 refresh_token을 사용해 자동으로 갱신할 수 있도록 함. + +### 1.1 배경 +- Gmail access_token은 1시간 후 만료 +- refresh_token을 사용하여 새 access_token 발급 필요 +- skill-email 서비스가 토큰 만료로 이메일 발송 실패하는 문제 해결 필요 + +--- + +## 2. 데이터베이스 구조 + +### 2.1 현재 DB 정보 +``` +Database: main_db (auth_db 아님 주의!) +Table: gmail_tokens +``` + +### 2.2 gmail_tokens 테이블 구조 +```sql +CREATE TABLE gmail_tokens ( + id SERIAL PRIMARY KEY, + user_id UUID UNIQUE NOT NULL, + robeing_id VARCHAR(50), + token_data JSONB NOT NULL, -- access_token, refresh_token, token_type + oauth_config JSONB, -- client_id, client_secret, token_uri + scopes JSONB, -- Gmail API 권한 목록 + metadata JSONB, -- email, source_file 등 + expiry TIMESTAMP, -- 토큰 만료 시간 + is_equipped BOOLEAN DEFAULT false, + equipped_to VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +### 2.3 현재 저장된 토큰 +| User ID | Email | 상태 | +|---------|-------|------| +| b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0 | 0914eagle@gmail.com | ❌ refresh_token 무효 | +| dddddddd-dddd-dddd-dddd-dddddddddddd | test@gmail.com | ✅ 정상 작동 | + +--- + +## 3. auth-server 자동 갱신 API + +### 3.1 구현 파일 +- `/home/heejae/auth-server/app/api/gmail_refresh.py` + +### 3.2 엔드포인트 + +#### 3.2.1 토큰 상태 확인 +``` +GET /api/gmail/check/{user_id} +``` + +**응답 예시:** +```json +{ + "status": "found", + "user_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", + "email": "test@gmail.com", + "has_token": true, + "has_access_token": true, + "has_refresh_token": true, + "has_oauth_config": true, + "is_expired": false, + "remaining_seconds": 34620, + "needs_refresh": false +} +``` + +#### 3.2.2 토큰 자동 갱신 +``` +POST /api/gmail/refresh/{user_id} +``` + +**응답 예시 (유효한 토큰):** +```json +{ + "status": "valid", + "user_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", + "email": "test@gmail.com", + "expires_in": 34550, + "access_token": "ya29.a0AS3H6Nxc8Z61FzHnEYmJ9TckEa0Yj8zd9Y6..." +} +``` + +**응답 예시 (갱신된 토큰):** +```json +{ + "status": "refreshed", + "user_id": "dddddddd-dddd-dddd-dddd-dddddddddddd", + "email": "test@gmail.com", + "expires_in": 3600, + "access_token": "ya29.a0AS3H6NxNEW_TOKEN_HERE..." +} +``` + +### 3.3 갱신 로직 +1. 토큰 만료 확인 (5분 이상 남았으면 갱신 안함) +2. refresh_token으로 Google OAuth API 호출 +3. 새 access_token 받아서 DB 업데이트 +4. 만료 시간(expiry) 업데이트 + +--- + +## 4. 문제 해결 과정 + +### 4.1 데이터베이스 문제 +- **문제**: auth_db가 존재하지 않음 +- **해결**: main_db 사용으로 변경 + +### 4.2 Docker 네트워크 문제 +- **문제**: Docker 컨테이너에서 localhost 접근 불가 +- **해결**: host.docker.internal 사용 + +### 4.3 refresh_token 무효 문제 +- **원인**: + - Google OAuth 앱이 테스트 모드 + - 6개월 이상 미사용 시 자동 만료 + - 사용자가 권한 취소 +- **해결**: 재인증 필요 + +--- + +## 5. 테스트 방법 + +### 5.1 토큰 상태 확인 +```bash +curl -X GET "http://localhost:9000/api/gmail/check/{user_id}" +``` + +### 5.2 토큰 갱신 +```bash +curl -X POST "http://localhost:9000/api/gmail/refresh/{user_id}" +``` + +### 5.3 로컬 테스트 스크립트 +```bash +python3 /home/heejae/test_refresh.py +``` + +--- + +## 6. Gmail 재인증 방법 + +### 6.1 OAuth 재인증 URL +``` +http://localhost:9000/auth/gmail/passport?user_id={user_id} +``` + +### 6.2 "액세스 차단됨" 에러 해결 +1. Google Cloud Console 접속 +2. APIs & Services → OAuth consent screen +3. Test users에 이메일 추가 +4. 또는 기존 권한 삭제 후 재시도 + - https://myaccount.google.com/permissions + +### 6.3 주의사항 +- gmail/login (로그인용) vs gmail/passport (API 권한용) 구분 +- passport 엔드포인트 사용해야 gmail.send 권한 획득 + +--- + +## 7. 환경 설정 + +### 7.1 auth-server 환경변수 (.env) +``` +DATABASE_URL=postgresql://robeings:robeings@host.docker.internal:5432/main_db +GOOGLE_CLIENT_ID=1044056803209-0h8n5kcl22rvl740mdgpejp58v27mlro.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=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 +``` + +### 7.2 Docker 실행 +```bash +cd /home/heejae/auth-server +docker compose down && docker compose up -d --build +``` + +--- + +## 8. 다음 단계 + +### 8.1 완료된 작업 +- [x] auth-server에 자동 갱신 API 구현 +- [x] test@gmail.com 계정 테스트 완료 +- [x] DB 연결 및 구조 확인 + +### 8.2 TODO +- [ ] 0914eagle@gmail.com 재인증 필요 +- [ ] skill-email이 auth-server 갱신 API 호출하도록 수정 +- [ ] gmail_passport.py를 현재 DB 구조(JSONB)에 맞게 수정 +- [ ] 토큰 만료 알림 기능 추가 고려 + +--- + +## 9. 참고사항 + +### 9.1 refresh_token 수명 +- **프로덕션 앱**: 6개월 미사용 시 만료 +- **테스트 앱**: 7일 후 만료 +- **해결**: 정기적 갱신 또는 프로덕션 모드 전환 + +### 9.2 민감한 스코프 +- gmail.send, gmail.modify는 제한된 스코프 +- Google 검증 필요할 수 있음 + +### 9.3 UUID 변경 이력 +- 원래: heejae, test, unknown (VARCHAR) +- 현재: UUID 형식으로 변경 +- heejae → b6ea2ee0-a15a-5cf4-93a9-a9ca20d4c4a0 + +--- + +## 10. 관련 문서 +- `/home/heejae/DOCS/250818_gmail_tokens_database_setup.md` +- `/home/heejae/DOCS/robeing-monitor-integration.md` +- `/home/heejae/DOCS/250817_slack_user_mapping_troubleshooting.md` \ No newline at end of file