Consolidate NAVER WORKS OAuth documentation - Remove redundant troubleshooting file, merge content into API guide
This commit is contained in:
parent
0d4f55e2af
commit
47265ebe76
@ -1,335 +0,0 @@
|
||||
# NAVER WORKS OAuth 2.0 구현 가이드
|
||||
|
||||
**작성일**: 2025-09-17
|
||||
**작성자**: happybell80
|
||||
**상태**: 구현 대기
|
||||
|
||||
## 1. 현재 상태 분석
|
||||
|
||||
### 1.1 기존 Slack OAuth 구현 확인
|
||||
**파일 위치**: `auth-server/app/providers/slack.py`
|
||||
|
||||
#### Slack OAuth 엔드포인트 구조
|
||||
```python
|
||||
# OIDC 사용자 로그인
|
||||
GET /auth/slack/login/ → slack_login()
|
||||
GET|POST /auth/slack/login/callback → slack_login_callback()
|
||||
|
||||
# Bot 설치 (Passport)
|
||||
GET /auth/slack/passport/install → slack_install()
|
||||
GET /auth/slack/passport/callback → slack_callback()
|
||||
GET /auth/slack/passport/status/{workspace_id} → slack_status()
|
||||
DELETE /auth/slack/passport/uninstall/{workspace_id} → slack_uninstall()
|
||||
```
|
||||
|
||||
#### Slack OAuth URL
|
||||
- **OIDC Authorize**: `https://slack.com/openid/connect/authorize`
|
||||
- **OIDC Token**: `https://slack.com/api/openid.connect.token`
|
||||
- **OIDC Userinfo**: `https://slack.com/api/openid.connect.userInfo`
|
||||
- **Bot Authorize**: `https://slack.com/oauth/v2/authorize`
|
||||
- **Bot Token**: `https://slack.com/api/oauth.v2.access`
|
||||
|
||||
#### Slack 구현 패턴
|
||||
1. State 생성 → Redis 저장 (TTL 300s)
|
||||
2. OAuth 리다이렉트
|
||||
3. Callback에서 state 검증
|
||||
4. Code → Token 교환
|
||||
5. Userinfo 조회
|
||||
6. DB User 매핑 (oauth_provider="slack", oauth_id=sub)
|
||||
7. JWT 생성 → Redis temp code 저장
|
||||
8. Frontend로 리다이렉트
|
||||
|
||||
### 1.2 NAVER WORKS 현재 설정
|
||||
**문서**: `DOCS/ideas/250916_네이버웍스_캘린더_API_연동_가이드.md`
|
||||
|
||||
- **앱 이름**: Ro-being
|
||||
- **소속**: company-x.partners (155032)
|
||||
- **Redirect URL (콘솔 설정)**: `https://auth.robeing.com/oauth/naverworks/callback`
|
||||
- **활성 Scopes**: openid, profile, email, calendar, contact, file, mail, task, user
|
||||
- **Token 설정**: Access Token 1시간, Refresh Token Rotation On
|
||||
|
||||
## 2. NAVER WORKS OAuth 2.0 구현 명세
|
||||
|
||||
### 2.1 공식 OAuth 엔드포인트
|
||||
**확인된 LINE WORKS API 2.0 엔드포인트**:
|
||||
- **Authorization**: `https://auth.worksmobile.com/oauth2/v2.0/authorize`
|
||||
- **Token**: `https://auth.worksmobile.com/oauth2/v2.0/token`
|
||||
- **Userinfo**: `https://www.worksapis.com/v1.0/oidc/userinfo`
|
||||
- **API Base**: `https://www.worksapis.com/v1.0/`
|
||||
|
||||
### 2.2 구현 필요 파일
|
||||
|
||||
#### 신규 생성
|
||||
1. **`auth-server/app/providers/naverworks.py`**
|
||||
```python
|
||||
from fastapi import APIRouter, Request, HTTPException, Depends
|
||||
from fastapi.responses import RedirectResponse
|
||||
import httpx
|
||||
import jwt
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from app.core.auth import create_access_token
|
||||
from app.models.user import User
|
||||
from app.core.config import settings
|
||||
from app.db.redis_client import redis_client
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
NAVERWORKS_AUTH_URL = "https://auth.worksmobile.com/oauth2/v2.0/authorize"
|
||||
NAVERWORKS_TOKEN_URL = "https://auth.worksmobile.com/oauth2/v2.0/token"
|
||||
NAVERWORKS_USERINFO_URL = "https://www.worksapis.com/v1.0/oidc/userinfo"
|
||||
NAVERWORKS_API_BASE = "https://www.worksapis.com/v1.0"
|
||||
|
||||
@router.get("/login/")
|
||||
async def naverworks_login(request: Request, redirect_uri: str = None):
|
||||
"""NAVER WORKS OAuth 로그인 시작"""
|
||||
# 구현 내용...
|
||||
|
||||
@router.get("/callback")
|
||||
@router.post("/callback") # form_post 지원
|
||||
async def naverworks_login_callback(request: Request):
|
||||
"""NAVER WORKS OAuth 콜백 처리"""
|
||||
# 구현 내용...
|
||||
|
||||
# Service Account JWT 인증 헬퍼
|
||||
async def get_service_account_token():
|
||||
"""서비스 계정으로 Access Token 발급"""
|
||||
# JWT assertion 생성
|
||||
# Token endpoint 호출
|
||||
# 구현 내용...
|
||||
```
|
||||
|
||||
#### 기존 파일 수정
|
||||
2. **`auth-server/app/main.py`**
|
||||
```python
|
||||
# 추가
|
||||
from app.providers import naverworks
|
||||
|
||||
# 라우터 등록
|
||||
app.include_router(
|
||||
naverworks.router,
|
||||
prefix="/auth/naverworks",
|
||||
tags=["auth-naverworks"]
|
||||
)
|
||||
```
|
||||
|
||||
3. **`auth-server/.env`**
|
||||
```bash
|
||||
# NAVER WORKS OAuth
|
||||
NAVERWORKS_CLIENT_ID=[Client ID from console]
|
||||
NAVERWORKS_CLIENT_SECRET=[Client Secret from console]
|
||||
NAVERWORKS_REDIRECT_URI=https://auth.ro-being.com/auth/naverworks/callback
|
||||
NAVERWORKS_SERVICE_ACCOUNT=[Service Account from console]
|
||||
NAVERWORKS_PRIVATE_KEY_PATH=/secure/naverworks/private_key.json
|
||||
```
|
||||
|
||||
### 2.3 데이터베이스 스키마
|
||||
|
||||
#### 신규 테이블 (선택적)
|
||||
```sql
|
||||
-- NAVER WORKS 전용 매핑 테이블 (필요시)
|
||||
CREATE TABLE naverworks_user_mapping (
|
||||
naverworks_user_id VARCHAR(100) NOT NULL,
|
||||
naverworks_org_id VARCHAR(100) NOT NULL,
|
||||
user_id UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (naverworks_user_id, naverworks_org_id)
|
||||
);
|
||||
|
||||
-- NAVER WORKS 토큰 저장 (Service Account용)
|
||||
CREATE TABLE naverworks_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
org_id VARCHAR(100) NOT NULL,
|
||||
access_token TEXT NOT NULL,
|
||||
refresh_token TEXT,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
#### 기존 users 테이블 활용
|
||||
- `oauth_provider`: "naverworks"
|
||||
- `oauth_id`: NAVER WORKS userId 또는 sub claim
|
||||
- `email`: 사용자 이메일
|
||||
- `name`: 사용자 이름
|
||||
|
||||
### 2.4 구현 플로우
|
||||
|
||||
#### 사용자 로그인 (OAuth 2.0 + OIDC)
|
||||
```
|
||||
1. GET /auth/naverworks/login/?redirect_uri={frontend_url}
|
||||
- State 생성 (UUID)
|
||||
- Redis 저장: oauth:state:{state} = {"redirect_uri", "created_at"}
|
||||
- Redirect to: https://auth.worksmobile.com/oauth2/v2.0/authorize
|
||||
?client_id={NAVERWORKS_CLIENT_ID}
|
||||
&redirect_uri={NAVERWORKS_REDIRECT_URI}
|
||||
&scope=openid+profile+email
|
||||
&response_type=code
|
||||
&state={state}
|
||||
|
||||
2. GET|POST /auth/naverworks/callback?code={code}&state={state}
|
||||
- State 검증 (Redis 조회 및 삭제)
|
||||
- Token 교환:
|
||||
POST https://auth.worksmobile.com/oauth2/v2.0/token
|
||||
{
|
||||
"code": code,
|
||||
"client_id": NAVERWORKS_CLIENT_ID,
|
||||
"client_secret": NAVERWORKS_CLIENT_SECRET,
|
||||
"redirect_uri": NAVERWORKS_REDIRECT_URI,
|
||||
"grant_type": "authorization_code"
|
||||
}
|
||||
- Userinfo 조회:
|
||||
GET https://www.worksapis.com/v1.0/oidc/userinfo
|
||||
Headers: Authorization: Bearer {access_token}
|
||||
- User 조회/생성:
|
||||
- oauth_provider="naverworks"
|
||||
- oauth_id={sub 또는 userId}
|
||||
- JWT 생성
|
||||
- 임시 코드 생성 → Redis: auth:temp:{temp_code}
|
||||
- Redirect: {redirect_uri}?code={temp_code}
|
||||
|
||||
3. Frontend: POST /auth/verify
|
||||
- 임시 코드로 JWT 토큰 수령
|
||||
```
|
||||
|
||||
#### Service Account 인증 (JWT)
|
||||
```
|
||||
1. Private Key 로드 (파일 시스템)
|
||||
2. JWT Assertion 생성:
|
||||
- iss: NAVERWORKS_CLIENT_ID
|
||||
- sub: NAVERWORKS_SERVICE_ACCOUNT
|
||||
- iat: 현재 시간
|
||||
- exp: 현재 시간 + 1시간
|
||||
3. Token 요청:
|
||||
POST https://auth.worksmobile.com/oauth2/v2.0/token
|
||||
{
|
||||
"assertion": jwt_assertion,
|
||||
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
"client_id": NAVERWORKS_CLIENT_ID,
|
||||
"client_secret": NAVERWORKS_CLIENT_SECRET,
|
||||
"scope": "calendar contact mail user"
|
||||
}
|
||||
4. Access Token 캐싱 (Redis, TTL 3600s)
|
||||
```
|
||||
|
||||
### 2.5 API 사용 예시
|
||||
|
||||
#### 캘린더 일정 생성
|
||||
```python
|
||||
async def create_calendar_event(user_id: str, event_data: dict):
|
||||
token = await get_service_account_token()
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
f"{NAVERWORKS_API_BASE}/users/{user_id}/calendar/events",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
json=event_data
|
||||
)
|
||||
return response.json()
|
||||
```
|
||||
|
||||
## 3. 구현 체크리스트
|
||||
|
||||
### 필수 작업
|
||||
- [ ] `auth-server/app/providers/naverworks.py` 생성
|
||||
- [ ] `auth-server/app/main.py`에 라우터 등록
|
||||
- [ ] `.env` 파일에 환경변수 추가
|
||||
- [ ] Redis key 패턴 구현 (oauth:state:*, auth:temp:*)
|
||||
- [ ] User 모델 매핑 로직
|
||||
- [ ] JWT 토큰 생성 및 검증
|
||||
|
||||
### 선택 작업
|
||||
- [ ] NAVER WORKS 전용 테이블 생성 (필요시)
|
||||
- [ ] Service Account JWT 인증 구현
|
||||
- [ ] Refresh Token 처리 로직
|
||||
- [ ] 에러 핸들링 및 재시도 로직
|
||||
|
||||
### Frontend 연동
|
||||
- [ ] 로그인 버튼 추가
|
||||
- [ ] `/auth/naverworks/login` 리다이렉트
|
||||
- [ ] 콜백 처리 및 토큰 저장
|
||||
|
||||
## 4. 보안 고려사항
|
||||
|
||||
### 비밀 키 관리
|
||||
- **Private Key 처리 완료** (2025-09-17):
|
||||
- Git에 임시 commit 후 서버 전송 완료
|
||||
- 서버 51123에 안전하게 저장
|
||||
- Git에서 즉시 삭제 (commit a4a2b9c)
|
||||
- 서버 경로: `/secure/naverworks/private_20250917185550.key`
|
||||
- Client Secret은 환경변수로만 관리
|
||||
- Private Key 파일 권한 600 설정 필수
|
||||
- **확인필요**: 한 번에 하나의 Private Key만 활성화 (콘솔 제약)
|
||||
|
||||
### 도메인 일치
|
||||
- **결정 완료**: `auth.ro-being.com` 사용
|
||||
- 콘솔 설정 변경 필요: auth.robeing.com → auth.ro-being.com
|
||||
- 기존 Slack과 통일
|
||||
|
||||
### 토큰 보안
|
||||
- Access Token은 Redis에 임시 저장 (TTL 설정)
|
||||
- Refresh Token은 암호화하여 DB 저장
|
||||
- Service Account 토큰은 게이트웨이 서버에서만 사용
|
||||
|
||||
## 5. 테스트 시나리오
|
||||
|
||||
### 로그인 플로우 테스트
|
||||
1. `/auth/naverworks/login` 접속
|
||||
2. NAVER WORKS 로그인 페이지 확인
|
||||
3. 인증 후 콜백 처리 확인
|
||||
4. JWT 토큰 발급 확인
|
||||
5. Frontend 리다이렉트 확인
|
||||
|
||||
### API 호출 테스트
|
||||
1. Service Account 토큰 발급
|
||||
2. 캘린더 API 호출 테스트
|
||||
3. 사용자 정보 조회 테스트
|
||||
4. 에러 처리 확인
|
||||
|
||||
## 6. 참고 자료
|
||||
|
||||
### 내부 문서
|
||||
- Slack OAuth 구현: `auth-server/app/providers/slack.py`
|
||||
- Gmail OAuth 구현: `auth-server/app/providers/gmail_passport.py`
|
||||
- NAVER WORKS 설정: `DOCS/ideas/250916_네이버웍스_캘린더_API_연동_가이드.md`
|
||||
|
||||
### 외부 문서
|
||||
- [LINE WORKS Developers](https://developers.worksmobile.com)
|
||||
- [OAuth 2.0 인증 가이드](https://developers.worksmobile.com/kr/docs/auth-oauth)
|
||||
- [API Reference](https://developers.worksmobile.com/kr/reference/introduction)
|
||||
|
||||
## 7. 구현 우선순위
|
||||
|
||||
1. **Phase 1** (즉시)
|
||||
- 환경변수 설정
|
||||
- 기본 OAuth 로그인 플로우
|
||||
- User 매핑 로직
|
||||
|
||||
2. **Phase 2** (1주 내)
|
||||
- Service Account 인증
|
||||
- 캘린더 API 연동
|
||||
- 에러 핸들링
|
||||
|
||||
3. **Phase 3** (2주 내)
|
||||
- Refresh Token 자동 갱신
|
||||
- 전체 API 통합
|
||||
- 모니터링 및 로깅
|
||||
|
||||
## 8. 미결 사항
|
||||
|
||||
### 결정 완료
|
||||
- [x] Redirect URL 도메인: `auth.ro-being.com` 사용
|
||||
- [x] Private Key: 서버 51123 `/secure/naverworks/` 저장
|
||||
- [x] 테이블: `naverworks_token` (team 스키마, 단수형)
|
||||
|
||||
### 확인 필요
|
||||
- [ ] NAVER WORKS OIDC userinfo endpoint 정확한 응답 형식
|
||||
- [ ] Service Account JWT 서명 알고리즘
|
||||
- [ ] Rate Limit 및 쿼터 제한
|
||||
- [ ] Private Key 교체 시 기존 토큰 유효성
|
||||
|
||||
---
|
||||
*작성: 2025-09-17 / happybell80*
|
||||
*다음 업데이트: 구현 완료 후*
|
||||
Loading…
x
Reference in New Issue
Block a user