2026-01-19 03:19:17 +08:00
|
|
|
|
"""
|
|
|
|
|
|
OAuth 认证模块
|
|
|
|
|
|
|
|
|
|
|
|
提供可配置的 OAuth 登录/绑定能力。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-01-30 12:43:08 +08:00
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2026-01-30 14:30:57 +08:00
|
|
|
|
from typing import TYPE_CHECKING, Any
|
2026-01-19 03:19:17 +08:00
|
|
|
|
|
|
|
|
|
|
from src.core.modules.base import (
|
|
|
|
|
|
ModuleCategory,
|
|
|
|
|
|
ModuleDefinition,
|
|
|
|
|
|
ModuleHealth,
|
|
|
|
|
|
ModuleMetadata,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 14:30:57 +08:00
|
|
|
|
def _get_router() -> Any:
|
2026-01-19 03:19:17 +08:00
|
|
|
|
"""延迟导入路由(避免启动时加载重依赖/副作用)。"""
|
|
|
|
|
|
# 延迟 discover,避免 alembic/mypy 等场景导入时触发 entry_points 解析
|
|
|
|
|
|
from src.services.auth.oauth.registry import get_oauth_provider_registry
|
|
|
|
|
|
|
|
|
|
|
|
get_oauth_provider_registry().discover_providers()
|
|
|
|
|
|
|
|
|
|
|
|
from src.api.oauth import router
|
|
|
|
|
|
|
|
|
|
|
|
return router
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _health_check() -> ModuleHealth:
|
|
|
|
|
|
# v1:不做外部网络探测,避免启动时阻塞
|
|
|
|
|
|
return ModuleHealth.UNKNOWN
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 03:10:21 +08:00
|
|
|
|
def _validate_config(db: Session) -> tuple[bool, str]:
|
2026-01-19 03:19:17 +08:00
|
|
|
|
"""
|
|
|
|
|
|
验证 OAuth 配置是否可以启用模块
|
|
|
|
|
|
|
|
|
|
|
|
检查项:
|
|
|
|
|
|
1. 至少有一个已启用的 Provider 配置
|
|
|
|
|
|
2. 已启用的 Provider 必须有 client_id 和 client_secret
|
|
|
|
|
|
"""
|
|
|
|
|
|
from src.models.database import OAuthProvider
|
|
|
|
|
|
|
|
|
|
|
|
# 查找所有已启用的 Provider
|
2026-02-01 17:28:00 +08:00
|
|
|
|
enabled_providers = db.query(OAuthProvider).filter(OAuthProvider.is_enabled.is_(True)).all()
|
2026-01-19 03:19:17 +08:00
|
|
|
|
|
|
|
|
|
|
if not enabled_providers:
|
|
|
|
|
|
return False, "请先配置并启用至少一个 OAuth Provider"
|
|
|
|
|
|
|
|
|
|
|
|
# 检查每个已启用的 Provider 配置完整性
|
|
|
|
|
|
for provider in enabled_providers:
|
|
|
|
|
|
if not provider.client_id:
|
|
|
|
|
|
return False, f"Provider [{provider.display_name}] 未配置 Client ID"
|
|
|
|
|
|
if not provider.client_secret_encrypted:
|
|
|
|
|
|
return False, f"Provider [{provider.display_name}] 未配置 Client Secret"
|
|
|
|
|
|
if not provider.redirect_uri:
|
|
|
|
|
|
return False, f"Provider [{provider.display_name}] 未配置回调地址"
|
|
|
|
|
|
|
|
|
|
|
|
return True, ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
oauth_module = ModuleDefinition(
|
|
|
|
|
|
metadata=ModuleMetadata(
|
|
|
|
|
|
name="oauth",
|
|
|
|
|
|
display_name="OAuth 登录",
|
|
|
|
|
|
description="支持通过第三方 OAuth Provider 登录/绑定账号",
|
|
|
|
|
|
category=ModuleCategory.AUTH,
|
|
|
|
|
|
env_key="OAUTH_AVAILABLE",
|
|
|
|
|
|
default_available=True,
|
|
|
|
|
|
required_packages=["httpx", "redis"],
|
|
|
|
|
|
api_prefix="/api/oauth",
|
|
|
|
|
|
admin_route="/admin/oauth",
|
|
|
|
|
|
admin_menu_icon="Key",
|
|
|
|
|
|
admin_menu_group="system",
|
|
|
|
|
|
admin_menu_order=55,
|
|
|
|
|
|
),
|
|
|
|
|
|
router_factory=_get_router,
|
|
|
|
|
|
health_check=_health_check,
|
|
|
|
|
|
validate_config=_validate_config,
|
|
|
|
|
|
)
|