10 KiB
10 KiB
NAVER WORKS OAuth 2.0 구현 가이드
작성일: 2025-09-17 작성자: happybell80 상태: 구현 대기
1. 현재 상태 분석
1.1 기존 Slack OAuth 구현 확인
파일 위치: auth-server/app/providers/slack.py
Slack OAuth 엔드포인트 구조
# 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 구현 패턴
- State 생성 → Redis 저장 (TTL 300s)
- OAuth 리다이렉트
- Callback에서 state 검증
- Code → Token 교환
- Userinfo 조회
- DB User 매핑 (oauth_provider="slack", oauth_id=sub)
- JWT 생성 → Redis temp code 저장
- 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 구현 필요 파일
신규 생성
auth-server/app/providers/naverworks.py
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 호출
# 구현 내용...
기존 파일 수정
auth-server/app/main.py
# 추가
from app.providers import naverworks
# 라우터 등록
app.include_router(
naverworks.router,
prefix="/auth/naverworks",
tags=["auth-naverworks"]
)
auth-server/.env
# 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 데이터베이스 스키마
신규 테이블 (선택적)
-- 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 claimemail: 사용자 이메일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 사용 예시
캘린더 일정 생성
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. 테스트 시나리오
로그인 플로우 테스트
/auth/naverworks/login접속- NAVER WORKS 로그인 페이지 확인
- 인증 후 콜백 처리 확인
- JWT 토큰 발급 확인
- Frontend 리다이렉트 확인
API 호출 테스트
- Service Account 토큰 발급
- 캘린더 API 호출 테스트
- 사용자 정보 조회 테스트
- 에러 처리 확인
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
외부 문서
7. 구현 우선순위
-
Phase 1 (즉시)
- 환경변수 설정
- 기본 OAuth 로그인 플로우
- User 매핑 로직
-
Phase 2 (1주 내)
- Service Account 인증
- 캘린더 API 연동
- 에러 핸들링
-
Phase 3 (2주 내)
- Refresh Token 자동 갱신
- 전체 API 통합
- 모니터링 및 로깅
8. 미결 사항
결정 완료
- Redirect URL 도메인:
auth.ro-being.com사용 - Private Key: 서버 51123
/secure/naverworks/저장 - 테이블:
naverworks_token(team 스키마, 단수형)
확인 필요
- NAVER WORKS OIDC userinfo endpoint 정확한 응답 형식
- Service Account JWT 서명 알고리즘
- Rate Limit 및 쿼터 제한
- Private Key 교체 시 기존 토큰 유효성
작성: 2025-09-17 / happybell80 다음 업데이트: 구현 완료 후