DOCS/journey/ideas/250810_auth_system_analysis.md
happybell80 0252dd1a7f fix: 51123 서버 IP 주소 업데이트 (성수 이전)
192.168.219.45 → 192.168.0.100 일괄 변경

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:52:26 +09:00

398 lines
12 KiB
Markdown

# 로빙 프로젝트 인증 시스템 전체 분석 및 통합 방안
## 현재 구조 분석
### 1. 데이터베이스 (PostgreSQL - main_db)
#### Users 테이블 구조
```sql
- id: UUID (Primary Key)
- username: VARCHAR(50) UNIQUE # happybell80
- email: VARCHAR(255) UNIQUE NOT NULL
- name: VARCHAR(255)
- picture: VARCHAR(500) # OAuth profile picture URL
- oauth_provider: VARCHAR(50) # google, github
- oauth_id: VARCHAR(255) # Provider의 user ID
- is_active: BOOLEAN
- created_at, updated_at, last_login_at: TIMESTAMP
```
#### WorkspaceMembers 테이블
```sql
- user_id: UUID (FK to users.id)
- workspace_id: UUID
- robeing_id: VARCHAR(100) # rb8001, rb10508
- role: ENUM (owner, member, guest)
```
### 2. Auth Server (51123 서버, 포트 9000)
#### JWT 토큰 구조
```python
# 토큰 생성 시 페이로드 (gmail.py:183-188)
{
"sub": username, # "happybell80" - 주요 식별자
"email": user_email, # "goeun2dc@gmail.com"
"name": user_name, # "김고은"
"username": username, # "happybell80" (중복)
"exp": expires_at
}
```
#### 환경 변수
- JWT_SECRET_KEY: `ro-being-auth-jwt-secret-key-2024`
- JWT_ALGORITHM: `HS256`
- JWT_EXPIRATION_DAYS: 30 (일) # 코드에서 사용
- JWT_ACCESS_TOKEN_EXPIRE_MINUTES: 30 (분) # .env에만 존재, 미사용
#### 이메일-유저명 매핑 (하드코딩)
```python
# gmail.py:150-154
email_to_username = {
'goeun2dc@gmail.com': 'happybell80',
'eagle0914@gmail.com': 'eagle0914',
}
```
### 3. Frontend (frontend-customer)
#### 토큰 저장/조회
- **저장 키**: `auth_token` (auth-context.tsx:61)
- **조회**: `localStorage.getItem('auth_token')` (auth-context.tsx:29)
- **서버 URL**: `https://auth.ro-being.com` (하드코딩)
#### 현재 문제점
```typescript
// robeing-api.ts - 토큰 전송 안함!
export async function sendMessage(text: string, userId: string = 'test_user') {
// Authorization 헤더 없음
// 모든 사용자가 'test_user'로 처리됨
}
```
### 4. Robeing Gateway (포트 8100)
#### 현재 처리 방식
```python
# main.py:92-95
x_user_id: Optional[str] = Header(None) # X-User-Id 헤더만 확인
# JWT 토큰 검증 없음!
```
### 5. Robeing Services (51124 서버)
#### ChromaDB 컬렉션명 생성
```python
# rb10508_micro/app/core/memory/storage.py
collection_name = f"{settings.ROBEING_ID}_{username if username else 'default'}_{memory_type}"
# 현재: rb10508_micro_default_episodic (모든 사용자 공유!)
# 문제: endpoints.py에서 username 전달하지 않음
# endpoints.py:22 - 기본값: "default_user"
```
#### 서비스 포트
- rb8001: 포트 8001
- rb10508_micro: 포트 10508
- rb10408_test: 포트 10408
#### 환경변수 현황
- AUTH_SERVER_URL: `http://192.168.0.100:9000` (설정됨)
- JWT_SECRET_KEY: `your-jwt-secret-key` (기본값, 변경 필요)
## 주요 문제점
### 1. ~~인증 토큰 미전송~~ ✅ 해결됨
- ~~Frontend가 API 호출 시 JWT 토큰을 전송하지 않음~~
- ~~`robeing-api.ts`에 Authorization 헤더 없음~~
- **수정 완료**: robeing-api.ts:40, chat-interface.tsx:275-279에서 토큰 전송 구현
### 2. 백엔드 토큰 미검증 ⚠️
- Robeing Gateway가 JWT 토큰을 검증하지 않음
- X-User-Id 헤더만 확인 (누구나 위조 가능)
### 3. 사용자 식별 실패 및 채널별 메모리 분리 ⚠️
- endpoints.py에서 username 파라미터를 storage.py로 전달하지 않음
- user_id는 받지만 username으로 변환하지 않음
- **채널별 다른 컬렉션 사용 문제**:
- Slack: `rb10508_micro_happybell80_episodic` (Auth 서버 매핑 제공)
- Frontend: `rb10508_micro_default_episodic` (모든 사용자 공유)
- **동일 사용자(happybell80)가 두 채널에서 다른 메모리 공간 사용**
- 대화 내용 분리로 연속성 상실
### 4. 이메일-유저명 매핑 하드코딩 📝
- 새 사용자 추가 시 코드 수정 필요
- 확장성 없음
### 5. OAuth 권한 과다 요청 🔐
- 회원가입/로그인 시 Gmail 읽기/쓰기 권한(`gmail.modify`)까지 요청
- 복잡한 동의 과정으로 사용자 이탈 가능성 높음
- 이메일 기능 미사용자에게도 과도한 권한 요청
### 6. 변수명 불일치 혼란 🔄
- user_id vs username vs email 혼용
- JWT의 'sub'에 username 저장 중 (gmail.py:184)
- endpoints.py는 user_id 사용, storage.py는 username 필요
### 7. Slack 통합과 인증 체계 불일치 🔗
- **Slack OAuth와 Google OAuth 별개 운영**
- Slack 워크스페이스 인증 (봇 토큰)
- Google 개인 인증 (사용자 토큰)
- 두 시스템 간 연결 고리 불명확
- **Slack 사용자 매핑 문제**
- slack_user_id (U0925SXQFDK) ↔ user_id (UUID) 매핑 필요
- 현재 하드코딩된 매핑 3개만 존재
- 신규 Slack 사용자 자동 연결 방법 없음
- **복잡한 관계 구조**
```
Slack Workspace ← SlackWorkspaces 테이블
Slack User ID ← slack_user_mapping 테이블
User (UUID) ← users 테이블
Google Account (OAuth)
```
- **권한 충돌 가능성**
- Slack 봇 권한: chat:write, channels:read 등
- Google 권한: gmail.modify, calendar.events 등
- 같은 사용자가 두 가지 인증 경로 보유
### 8. JWT Secret 불일치 🔑
- Auth Server: `ro-being-auth-jwt-secret-key-2024`
- Robeing Services: `your-jwt-secret-key` (기본값)
- 토큰 검증 시 실패할 수 있음
## 결정 필요 사항
### 1. 주요 식별자 선택
- [ ] **username** (추천): 'happybell80' - 인간 친화적, URL 안전
- [ ] **user_id (UUID)**: 'a1b2c3d4-...' - DB 표준, 중복 없음
- [ ] **email**: 'goeun2dc@gmail.com' - OAuth 표준, 변경 가능성
### 2. JWT 페이로드 표준화
```json
옵션 A (username 중심):
{
"sub": "happybell80", // 주 식별자
"user_id": "uuid-here", // DB ID
"email": "goeun2dc@gmail.com",
"name": "김고은"
}
옵션 B (UUID 중심):
{
"sub": "uuid-here", // 주 식별자
"username": "happybell80",
"email": "goeun2dc@gmail.com",
"name": "김고은"
}
```
### 3. API 헤더 표준화
```
옵션 A (Bearer 토큰만):
Authorization: Bearer <jwt_token>
옵션 B (Bearer + X-User-Id):
Authorization: Bearer <jwt_token>
X-User-Id: happybell80 // 빠른 조회용
옵션 C (Bearer + X-Username):
Authorization: Bearer <jwt_token>
X-Username: happybell80 // 명확한 이름
```
### 4. ChromaDB 컬렉션 네이밍
```
옵션 A: {robeing_id}_{username}_episodic
예: rb10508_happybell80_episodic
옵션 B: {robeing_id}_{user_uuid}_episodic
예: rb10508_a1b2c3d4_episodic
옵션 C: {workspace_id}_{robeing_id}_{username}_episodic
예: workspace1_rb10508_happybell80_episodic
```
### 5. 토큰 저장 키 통일
```javascript
현재: 'auth_token'
대안: 'jwt_token', 'access_token', 'robeing_token'
```
### 6. OAuth 권한 분리 전략
```python
# 옵션 A: 단계별 권한 요청 (추천) ✅
기본 로그인: openid, userinfo.email, userinfo.profile
이메일 스킬: gmail.modify (별도 연결)
캘린더 스킬: calendar.events (별도 연결)
# 옵션 B: 일괄 권한 요청 (현재)
모든 권한을 한 번에 요청 (사용자 이탈 높음)
```
**구현 방안:**
- `/auth/gmail/login` - 기본 프로필 권한만 (간단한 로그인)
- `/auth/gmail/connect-email` - Gmail 권한 추가 (이메일 아이템 사용 시)
- DB에 권한 레벨 저장: `oauth_scopes` 필드 추가
- Frontend에서 권한 상태 표시 및 추가 연결 버튼 제공
### 7. Slack과 Google 인증 통합 전략
```
옵션 A: 통합 인증 (하나의 주 계정)
- Google 로그인을 주 인증으로 사용
- Slack은 추가 연결 (선택적)
- 장점: 단순한 인증 흐름
- 단점: Slack 전용 사용자 지원 어려움
옵션 B: 독립 인증 (현재)
- Google과 Slack 각각 독립 인증
- 수동 매핑 필요
- 장점: 유연성
- 단점: 복잡한 관리
옵션 C: 자동 연결 (추천) ✅
- 이메일 기반 자동 매칭
- Slack 이메일 == Google 이메일 시 자동 연결
- 불일치 시 수동 연결 옵션 제공
```
**구현 방안:**
- Slack 사용자가 처음 메시지 전송 시:
1. Slack API로 이메일 조회
2. users 테이블에서 같은 이메일 검색
3. 매칭되면 자동으로 slack_user_mapping 생성
4. 없으면 임시 계정 생성 후 연결 대기
## 즉시 수정 필요 (Critical)
### 통합 해결 방안 (우선순위별)
#### 1. Auth 서버 JWT 활용 (권장) 🌟
```
Frontend 로그인 → JWT(username 포함) → Gateway 검증 → Robeing username 추출
→ Slack과 동일한 컬렉션 사용 (rb10508_micro_happybell80_episodic)
```
#### 2. Frontend user_id 전달 개선
```
로그인 후 username을 user_id로 전송
현재: user_id = "default_user"
개선: user_id = "happybell80"
```
#### 3. 통합 매핑 테이블 구축
```
Auth 서버에 통합 매핑:
email ↔ slack_id ↔ username
- Frontend: email → username
- Slack: slack_id → username
- 결과: 동일 username으로 통합 메모리
```
**관련 문서**: [Auth DB 테이블 구조 단순화 방안](/plans/250812_main_db_테이블_단순화_방안.md)
##### 현재 테이블 구조 (5개)
- **users**: 사용자 기본 정보 (email, username)
- **workspaces**: 워크스페이스 정보 (로빙 할당)
- **workspace_member**: 사용자-워크스페이스 연결
- **slack_workspaces**: Slack 워크스페이스 정보
- **slack_user_mapping**: Slack ID ↔ User ID 매핑 (3개만 존재)
##### 제안된 통합 구조 (4개 테이블로 단순화)
```sql
-- 1. users: 기존 유지
-- 2. slack_workspaces: 단순화
-- 3. user_robeings: 사용자-로빙 직접 연결
CREATE TABLE user_robeings (
user_id UUID REFERENCES user(id),
robeing_id VARCHAR(100), -- rb10508_micro 등
is_primary BOOLEAN
);
-- 4. slack_users: 통합 매핑 테이블 (핵심!)
CREATE TABLE slack_users (
slack_user_id VARCHAR(100), -- U0925SXQFDK
slack_team_id VARCHAR(100),
user_id UUID REFERENCES user(id),
robeing_id VARCHAR(100) -- Slack별 로빙 지정 가능
);
```
### 개별 수정 사항
#### 1. ~~Frontend (robeing-api.ts)~~ ✅ 완료
```typescript
// 수정 완료 - robeing-api.ts:38-41
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
```
#### 2. Gateway (main.py)
```python
# JWT 검증 추가
from app.core.auth import decode_access_token
authorization = Header(None)
if authorization:
token = authorization.replace("Bearer ", "")
payload = decode_access_token(token)
username = payload.get("sub") # 또는 username
```
#### 3. Robeing Service (rb10508_micro)
```python
# endpoints.py 수정 필요:
# 1. JWT 토큰에서 username 추출
# 2. storage 함수 호출 시 username 전달
# storage.py는 이미 준비됨:
collection_name = f"{settings.ROBEING_ID}_{username if username else 'default'}_{memory_type}"
```
## 단계별 구현 계획
### Phase 1: 긴급 패치
1. ~~Frontend에서 토큰 전송~~ ✅ 완료
2. Gateway에서 토큰 검증
3. username 기반 ChromaDB 분리
### Phase 2: 표준화
1. 변수명 통일 (username vs user_id)
2. JWT 페이로드 표준화
3. API 문서 작성
### Phase 3: 개선
1. 이메일-유저명 매핑 DB화
2. 토큰 갱신 로직 구현
3. 권한 체계 구현
### Phase 4: OAuth 권한 분리
1. 기본 로그인용 엔드포인트 구현 (최소 권한)
2. 스킬별 추가 권한 연결 엔드포인트 구현
3. DB에 사용자별 권한 상태 저장
4. Frontend에 권한 관리 UI 추가
### Phase 5: Slack-Google 인증 통합
1. 이메일 기반 자동 매칭 로직 구현
2. Slack 사용자 첫 메시지 시 자동 계정 생성
3. 수동 연결 UI 개발
4. 통합 대시보드 구현
## 참고 사항
- **JWT Secret**: 모든 서비스가 같은 키 공유 필요
- **CORS 설정**: frontend URL 추가 필요
- **Redis**: 토큰 캐싱 및 세션 관리 활용
- **로그**: 모든 인증 실패 상세 로깅
## 문서 작성자
- 작성일: 2025-08-16
- 작성자: 51123 서버 관리 Claude
- 목적: 인증 시스템 통합을 위한 현황 분석