Files
Aether/_deprecated_py_src/services/auth/oauth/base.py
fawney19 1d9c77522a refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
2026-04-03 16:26:16 +08:00

112 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from urllib.parse import urlencode, urlparse, urlunparse
import httpx
from src.services.auth.oauth.models import OAuthFlowError, OAuthToken, OAuthUserInfo
if TYPE_CHECKING:
from src.models.database import OAuthProvider
class OAuthProviderBase(ABC):
"""
OAuth Provider 基类(稳定扩展点)。
v1 收敛点:仅实现 OAuth2 授权码流程所需的最小接口。
"""
provider_type: str
display_name: str
# 允许的 host 白名单(用于端点覆盖校验,支持子域名)
allowed_domains: tuple[str, ...] = ()
authorization_url: str
token_url: str
userinfo_url: str
default_scopes: tuple[str, ...] = ()
def get_effective_authorization_url(self, config: OAuthProvider) -> str:
return config.authorization_url_override or self.authorization_url
def get_effective_token_url(self, config: OAuthProvider) -> str:
return config.token_url_override or self.token_url
def get_effective_userinfo_url(self, config: OAuthProvider) -> str:
return config.userinfo_url_override or self.userinfo_url
def get_effective_scopes(self, config: OAuthProvider) -> str:
scopes = config.scopes or list(self.default_scopes)
return " ".join(scopes)
def get_authorization_url(self, config: OAuthProvider, state: str) -> str:
"""
构造 provider 授权 URL。
redirect_uri 必须由服务端控制,不从客户端传入。
"""
base = self.get_effective_authorization_url(config)
# 避免覆盖原有 query若 provider 默认 url 带 query保留
parsed = urlparse(base)
query: dict[str, str] = {}
if parsed.query:
# 保留已有 query 参数
for kv in parsed.query.split("&"):
if not kv:
continue
if "=" in kv:
k, v = kv.split("=", 1)
query[k] = v
else:
query[kv] = ""
client_id = config.client_id
redirect_uri = config.redirect_uri
if not client_id or not redirect_uri:
raise ValueError("OAuthProvider 配置不完整client_id/redirect_uri 不能为空")
query.update(
{
"response_type": "code",
"client_id": client_id,
"redirect_uri": redirect_uri,
"state": state,
}
)
scopes = self.get_effective_scopes(config)
if scopes:
query["scope"] = scopes
return urlunparse(parsed._replace(query=urlencode(query)))
@abstractmethod
async def exchange_code(self, config: OAuthProvider, code: str) -> OAuthToken:
"""使用授权码兑换 token。"""
@abstractmethod
async def get_user_info(self, config: OAuthProvider, access_token: str) -> OAuthUserInfo:
"""获取用户信息。"""
async def _http_post_form(
self,
url: str,
data: dict[str, str],
*,
timeout_seconds: float = 5.0,
headers: dict[str, str] | None = None,
) -> httpx.Response:
raise OAuthFlowError("provider_unavailable", "OAuth 仅支持 Rust executor")
async def _http_get(
self,
url: str,
*,
timeout_seconds: float = 5.0,
headers: dict[str, str] | None = None,
) -> httpx.Response:
raise OAuthFlowError("provider_unavailable", "OAuth 仅支持 Rust executor")