- OAuth 2.0 서비스 구현 * Authorization Code, Client Credentials, Refresh Token 플로우 지원 * 애플리케이션 등록 및 관리 기능 * 토큰 introspection 및 revocation * SSO 설정 지원 (Google, GitHub, SAML) * 실용적인 스코프 시스템 (user, app, org, api 관리) - 사용자 프로필 기능 확장 * 프로필 사진 및 썸네일 필드 추가 * bio, location, website 등 추가 프로필 정보 * 이메일 인증 및 계정 활성화 상태 관리 * UserPublicResponse 모델 추가 - OAuth 스코프 관리 * picture 스코프 추가 (프로필 사진 접근 제어) * 카테고리별 스코프 정리 (기본 인증, 사용자 데이터, 앱 관리, 조직, API) * 스코프별 승인 필요 여부 설정 - 인프라 개선 * Users 서비스 포트 매핑 추가 (8001) * OAuth 서비스 Docker 구성 (포트 8003) * Kafka 이벤트 통합 (USER_CREATED, USER_UPDATED, USER_DELETED) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
5.7 KiB
Python
126 lines
5.7 KiB
Python
from beanie import Document, PydanticObjectId
|
|
from pydantic import BaseModel, Field, EmailStr
|
|
from typing import Optional, List, Dict
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
class GrantType(str, Enum):
|
|
AUTHORIZATION_CODE = "authorization_code"
|
|
CLIENT_CREDENTIALS = "client_credentials"
|
|
PASSWORD = "password"
|
|
REFRESH_TOKEN = "refresh_token"
|
|
|
|
class ResponseType(str, Enum):
|
|
CODE = "code"
|
|
TOKEN = "token"
|
|
|
|
class TokenType(str, Enum):
|
|
BEARER = "Bearer"
|
|
|
|
class OAuthApplication(Document):
|
|
"""OAuth 2.0 클라이언트 애플리케이션"""
|
|
client_id: str = Field(..., unique=True, description="클라이언트 ID")
|
|
client_secret: str = Field(..., description="클라이언트 시크릿 (해시됨)")
|
|
name: str = Field(..., description="애플리케이션 이름")
|
|
description: Optional[str] = Field(None, description="애플리케이션 설명")
|
|
|
|
owner_id: str = Field(..., description="애플리케이션 소유자 ID")
|
|
|
|
redirect_uris: List[str] = Field(default_factory=list, description="허용된 리다이렉트 URI들")
|
|
allowed_scopes: List[str] = Field(default_factory=list, description="허용된 스코프들")
|
|
grant_types: List[GrantType] = Field(default_factory=lambda: [GrantType.AUTHORIZATION_CODE], description="허용된 grant types")
|
|
|
|
is_active: bool = Field(default=True, description="활성화 상태")
|
|
is_trusted: bool = Field(default=False, description="신뢰할 수 있는 앱 (자동 승인)")
|
|
|
|
# SSO 설정
|
|
sso_enabled: bool = Field(default=False, description="SSO 활성화 여부")
|
|
sso_provider: Optional[str] = Field(None, description="SSO 제공자 (google, github, saml 등)")
|
|
sso_config: Optional[Dict] = Field(default_factory=dict, description="SSO 설정 (provider별 설정)")
|
|
allowed_domains: List[str] = Field(default_factory=list, description="SSO 허용 도메인 (예: @company.com)")
|
|
|
|
website_url: Optional[str] = Field(None, description="애플리케이션 웹사이트")
|
|
logo_url: Optional[str] = Field(None, description="애플리케이션 로고 URL")
|
|
privacy_policy_url: Optional[str] = Field(None, description="개인정보 처리방침 URL")
|
|
terms_url: Optional[str] = Field(None, description="이용약관 URL")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
updated_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
class Settings:
|
|
collection = "oauth_applications"
|
|
|
|
class AuthorizationCode(Document):
|
|
"""OAuth 2.0 인증 코드"""
|
|
code: str = Field(..., unique=True, description="인증 코드")
|
|
client_id: str = Field(..., description="클라이언트 ID")
|
|
user_id: str = Field(..., description="사용자 ID")
|
|
|
|
redirect_uri: str = Field(..., description="리다이렉트 URI")
|
|
scopes: List[str] = Field(default_factory=list, description="요청된 스코프")
|
|
|
|
code_challenge: Optional[str] = Field(None, description="PKCE code challenge")
|
|
code_challenge_method: Optional[str] = Field(None, description="PKCE challenge method")
|
|
|
|
expires_at: datetime = Field(..., description="만료 시간")
|
|
used: bool = Field(default=False, description="사용 여부")
|
|
used_at: Optional[datetime] = Field(None, description="사용 시간")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
class Settings:
|
|
collection = "authorization_codes"
|
|
|
|
class AccessToken(Document):
|
|
"""OAuth 2.0 액세스 토큰"""
|
|
token: str = Field(..., unique=True, description="액세스 토큰")
|
|
refresh_token: Optional[str] = Field(None, description="리프레시 토큰")
|
|
|
|
client_id: str = Field(..., description="클라이언트 ID")
|
|
user_id: Optional[str] = Field(None, description="사용자 ID (client credentials flow에서는 없음)")
|
|
|
|
token_type: TokenType = Field(default=TokenType.BEARER)
|
|
scopes: List[str] = Field(default_factory=list, description="부여된 스코프")
|
|
|
|
expires_at: datetime = Field(..., description="액세스 토큰 만료 시간")
|
|
refresh_expires_at: Optional[datetime] = Field(None, description="리프레시 토큰 만료 시간")
|
|
|
|
revoked: bool = Field(default=False, description="폐기 여부")
|
|
revoked_at: Optional[datetime] = Field(None, description="폐기 시간")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
last_used_at: Optional[datetime] = Field(None, description="마지막 사용 시간")
|
|
|
|
class Settings:
|
|
collection = "access_tokens"
|
|
|
|
class OAuthScope(Document):
|
|
"""OAuth 스코프 정의"""
|
|
name: str = Field(..., unique=True, description="스코프 이름 (예: read:profile)")
|
|
display_name: str = Field(..., description="표시 이름")
|
|
description: str = Field(..., description="스코프 설명")
|
|
|
|
is_default: bool = Field(default=False, description="기본 스코프 여부")
|
|
requires_approval: bool = Field(default=True, description="사용자 승인 필요 여부")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
|
|
class Settings:
|
|
collection = "oauth_scopes"
|
|
|
|
class UserConsent(Document):
|
|
"""사용자 동의 기록"""
|
|
user_id: str = Field(..., description="사용자 ID")
|
|
client_id: str = Field(..., description="클라이언트 ID")
|
|
|
|
granted_scopes: List[str] = Field(default_factory=list, description="승인된 스코프")
|
|
|
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
updated_at: datetime = Field(default_factory=datetime.now)
|
|
expires_at: Optional[datetime] = Field(None, description="동의 만료 시간")
|
|
|
|
class Settings:
|
|
collection = "user_consents"
|
|
indexes = [
|
|
[("user_id", 1), ("client_id", 1)]
|
|
] |