# 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 저장 위치 (파일 시스템 vs Vault) - Client Secret은 환경변수로만 관리 - Private Key 파일 권한 600 설정 필수 - **확인필요**: 한 번에 하나의 Private Key만 활성화 (콘솔 제약) ### 도메인 일치 - **결정필요**: Redirect URL 도메인 통일 - 콘솔 설정: `auth.robeing.com` - 현재 Slack: `auth.ro-being.com` - 하나로 통일 필요 ### 토큰 보안 - 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. 미결 사항 ### 결정 필요 - [ ] Redirect URL 도메인 (auth.ro-being.com vs auth.robeing.com) - [ ] Private Key 저장 방식 (파일 vs Vault) - [ ] NAVER WORKS 전용 테이블 생성 여부 ### 확인 필요 - [ ] NAVER WORKS OIDC userinfo endpoint 정확한 응답 형식 - [ ] Service Account JWT 서명 알고리즘 - [ ] Rate Limit 및 쿼터 제한 - [ ] Private Key 교체 시 기존 토큰 유효성 --- *작성: 2025-09-17 / happybell80* *다음 업데이트: 구현 완료 후*