# turbo-agent-auth 设计文档

## 1. 架构概览

### 1.1 设计目标

turbo-agent-auth 旨在提供一个通用的、可配置的认证授权框架，满足以下核心需求：

1. **零代码集成**：通过配置而非编码完成不同平台的认证对接
2. **多协议支持**：统一处理 JWT、OAuth1、OAuth2、API Key、Cookies、SMTP、POP3 等多种认证方式
3. **多阶段流程**：支持复杂的多步骤认证流程（如 CSRF Token → Session Cookie）
4. **完整可观测**：记录每个字段的来源、执行轨迹、失败原因
5. **高内聚低耦合**：核心逻辑与数据库解耦，可独立使用

### 1.2 三层架构

```
┌─────────────────────────────────────────────────────────────┐
│                    应用层 (API Routes)                        │
│  - REST API 端点 (FastAPI)                                   │
│  - 参数验证 (Pydantic)                                       │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                  服务层 (auth_service.py)                     │
│  - 平台/授权方式/秘钥的 CRUD                                  │
│  - 数据持久化 (Prisma)                                       │
│  - 集成核心引擎执行认证流程                                   │
│  - 执行轨迹记录与状态管理                                     │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                    核心层 (auth_core.py)                      │
│  - AuthFlowExecutor: 流程编排引擎                            │
│  - parse_response_data: 响应解析引擎                         │
│  - prepare_login_request_config: 请求构建器                  │
│  - 零数据库依赖，纯函数式设计                                 │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                  客户端层 (client_helper.py)                  │
│  - AuthClient / AsyncAuthClient                             │
│  - 完整的 HTTP 客户端实现                                     │
│  - 自动认证注入                                               │
│  - 独立使用，无需服务端                                       │
└─────────────────────────────────────────────────────────────┘
```

## 2. 核心设计

### 2.1 AuthFlowExecutor：基于生成器的流程编排

#### 设计理念

使用 Python Generator 实现控制流反转，让同一套逻辑可以驱动同步和异步 HTTP 请求。

#### 工作原理

```python
class AuthFlowExecutor:
    def execute(self) -> Generator[Dict[str, Any], ResponseData, LoginExecutionResult]:
        """
        1. yield 请求配置 -> 调用者执行 HTTP 请求
        2. 接收 ResponseData -> 解析并更新上下文
        3. 继续下一步或返回最终结果
        """
```

**优势**：
- 调用者控制 I/O（同步/异步/Mock）
- 逻辑复用：同一个 executor 可被 `AuthClient`、`AsyncAuthClient`、`AuthService` 使用
- 易测试：可注入模拟响应数据

**示例流程**：

```
[Executor]                [Caller]
   |                         |
   |-- yield req_cfg_1 ----->|
   |                         |---> HTTP POST /login
   |<---- ResponseData_1 ----|
   |                         |
   | (解析 csrf_token)        |
   |                         |
   |-- yield req_cfg_2 ----->|
   |                         |---> HTTP POST /login (with csrf)
   |<---- ResponseData_2 ----|
   |                         |
   | (解析 access_token)      |
   |                         |
   |-- return result -------->|
```

### 2.2 响应解析引擎

#### parse_response_data 功能

支持从多种来源提取数据：

| 来源 | 配置示例 | 说明 |
|------|---------|------|
| **JSON Body** | `{"access_token": "access_token"}` | JSONPath 提取 |
| **Headers** | `{"request_id": {"name": "X-Request-ID"}}` | 支持数组索引 |
| **Cookies** | `{"session": "session"}` | 自动合并 Set-Cookie |
| **HTML** | `{"csrf": {"selector": {"tag": "input", "attrs": {"name": "csrf"}}, "attribute": "value"}}` | CSS 选择器 + 正则 |

#### 过期时间推导

综合多个来源，取最早过期时间：

1. **Token 过期**：`expires_in`（秒）或 `expires_at`（ISO 时间戳）
2. **Cookie 过期**：解析 `Set-Cookie` 的 `Max-Age` 或 `Expires`
3. **默认过期**：`AuthMethod.defaultValiditySeconds`

```python
if token_expires_at and cookie_expires_at:
    final_expires_at = min(token_expires_at, cookie_expires_at)
```

#### 字段来源追踪

每个提取的字段记录来源路径：

```json
{
  "extracted": {
    "access_token": "abc123",
    "session": "xyz789",
    "csrf_token": "def456"
  },
  "fieldSources": {
    "access_token": "body.access_token",
    "session": "headers.set-cookie",
    "csrf_token": "html.selector"
  }
}
```

### 2.3 请求构建器

#### prepare_login_request_config 职责

1. **模板渲染**：将 `loginPayload` 代入 `{{placeholder}}`
2. **运行时注入**：自动添加 `runtime.callback_url`、`runtime.ip_allow_list`
3. **参数放置**：根据 `requestPlacements` 将上一步结果注入当前请求

#### 模板语法

```yaml
body_template:
  username: "{{username}}"           # 直接引用
  callback: "{{runtime.callback_url}}" # 运行时值
  token: "{{cookies.session}}"       # 上一步结果
```

#### requestPlacements 示例

```json
{
  "requestPlacements": {
    "headers": {
      "X-CSRF-Token": "csrf_token"
    },
    "cookies": {
      "session": "cookies.session"
    },
    "body": {
      "username": "username",
      "password": "password",
      "csrf_token": "csrf_token"
    }
  }
}
```

## 3. 数据模型

### 3.0 API 与 Core 对齐

API 层的输入/输出模型已与 `turbo-agent-core` 对齐：

| API 字段 | Core 字段 | 数据库字段 | 说明 |
|---------|----------|-----------|------|
| `authType` | `authType` | `type` | 认证类型（AuthMethodCreate/Out） |
| `credential` | `credential` | `data` | 秘钥凭证（SecretUpsert/Update/Out） |

**字段映射逻辑**：
- **AuthMethod**: API 接收 `authType`，服务层映射到数据库的 `type` 字段
- **Secret**: API 接收/返回 `credential`，服务层自动映射到数据库的 `data` 字段
- **Pydantic 验证器**: `SecretOut` 等模型使用 `@model_validator(mode='before')` 在序列化时自动填充 Core 字段

这种设计确保：
1. 外部 API 与 Core 模型语义一致
2. 内部数据库 Schema 保持稳定（避免迁移）
3. 客户端代码可直接使用 Core 类型

### 3.1 Platform（平台）

```prisma
model Platform {
  id          String       @id
  orgId       String
  nameId      String
  name        String
  code        String
  baseUrl     String?
  authMethods AuthMethod[]
}
```

**用途**：组织和分类认证方式

### 3.2 AuthMethod（认证方法）

```prisma
model AuthMethod {
  id                     String   @id
  platformId             String
  name                   String
  type                   AuthType  # Internal DB field
  loginFlow              Json?
  loginFieldsSchema      Json?
  refreshFlow            Json?
  refreshFieldsSchema    Json?
  authFieldsSchema       Json?
  authFieldPlacements    Json?
  responseMapping        Json?
  defaultValiditySeconds Int?
  refreshBeforeSeconds   Int?
  secrets                Secret[]
}
```

**核心字段说明**：

- `loginFlow`: 登录请求配置（可含多步骤 `steps`）
- `responseMapping`: 如何从响应中提取字段
- `authFieldPlacements`: 业务请求时如何注入认证信息

### 3.3 Secret（秘钥）

```prisma
model Secret {
  id                String    @id
  authMethodId      String
  name              String
  data              Json?      # 认证凭证 (API 暴露为 credential)
  authDataSources   Json?      # 字段来源
  loginPayload      Json?      # 登录表单
  autoLoginEnabled  Boolean    @default(false)
  expiresAt         DateTime?
  status            String     @default("pending")
  isValid           Boolean    @default(true)
  lastRefreshed     DateTime?
  lastLoginAt       DateTime?
  lastLoginStatus   String?
  lastLoginError    String?
}
```

**生命周期**：

```
[创建] -> [登录] -> [激活] -> [使用] -> [刷新] -> [失效] -> [删除]
```

## 4. 关键流程

### 4.1 多阶段登录流程（以 Superset 为例）

#### 场景描述

Superset 需要两次请求：
1. 第一次获取 CSRF Token（从 HTML 和 Cookie）
2. 第二次携带 CSRF Token 完成登录

#### 配置示例

```json
{
  "loginFlow": {
    "steps": [
      {
        "name": "get_csrf",
        "url": "https://superset.example.com/login/",
        "method": "POST",
        "body_template": {
          "username": "{{username}}",
          "password": "{{password}}"
        },
        "responseMapping": {
          "html": {
            "csrf_token": {
              "selector": {"tag": "input", "attrs": {"name": "csrf_token"}},
              "attribute": "value"
            }
          },
          "cookies": {
            "session": "session"
          }
        }
      },
      {
        "name": "submit_with_csrf",
        "url": "https://superset.example.com/login/",
        "method": "POST",
        "requestPlacements": {
          "cookies": {"session": "cookies.session"},
          "body": {
            "username": "username",
            "password": "password",
            "csrf_token": "csrf_token"
          }
        }
      }
    ]
  }
}
```

#### 执行流程

```
1. Executor yields step_1_config
   └─> Caller: POST /login/ (username, password)
       └─> Response: HTML with csrf_token + Set-Cookie: session=xxx

2. Executor parses response:
   └─> csrf_token = "abc123" (from HTML)
   └─> cookies.session = "xyz789" (from Set-Cookie)
   └─> Updates context: {username, password, csrf_token, cookies: {session}}

3. Executor yields step_2_config:
   └─> Method: POST
   └─> URL: /login/
   └─> Cookies: {session: "xyz789"}
   └─> Body: {username, password, csrf_token: "abc123"}

4. Caller: POST /login/ (with all data)
   └─> Response: 302 Redirect + Set-Cookie: session=new_session

5. Executor parses final response:
   └─> Updates secret.data = {cookies: {session: "new_session"}}
   └─> Calculates expiresAt from Cookie Max-Age
```

### 4.2 客户端认证注入流程

#### 使用场景

用户使用 `AuthClient` 发起业务请求，系统自动注入认证信息。

#### 执行步骤

```python
client = AuthClient(platform, auth_method, secret)

# 1. 用户调用
response = client.get("/api/data", params={"page": 1})

# 2. 内部流程
# 2.1 调用 prepare_http() 解析 authFieldPlacements
applied = {
    "headers": {"Authorization": "Bearer abc123"},
    "query": {},
    "cookies": {"session": "xyz789"},
    "body": {}
}

# 2.2 合并用户参数
final_params = {
    "headers": {"Authorization": "Bearer abc123"},
    "params": {"page": 1},
    "cookies": {"session": "xyz789"}
}

# 2.3 发起请求
response = httpx.get(base_url + "/api/data", **final_params)
```

## 5. 可观测性设计

### 5.1 日志分层

| 级别 | 事件 | 内容 |
|------|------|------|
| **DEBUG** | 登录表单 | 脱敏后的 `loginPayload`（需开启 `LOG_SENSITIVE_DEBUG`） |
| **INFO** | 步骤执行 | 方法、URL、状态码、耗时 |
| **INFO** | 最终结果 | 提取的字段名、过期时间 |
| **WARNING** | 4xx 错误 | 请求失败，客户端错误 |
| **ERROR** | 5xx 错误 | 服务端错误、异常堆栈 |

### 5.2 执行轨迹

通过 `/login/trace` 或 `/with-execution` 获取完整执行记录：

```json
{
  "steps": [
    {
      "index": 0,
      "name": "get_csrf",
      "request": {
        "method": "POST",
        "url": "https://superset.example.com/login/",
        "headers": {},
        "params": {},
        "json": {"username": "admin", "password": "***"}
      },
      "statusCode": 200,
      "ok": true,
      "durationMs": 342,
      "extracted": {
        "csrf_token": "abc123",
        "cookies": {"session": "xyz789"}
      },
      "fieldSources": {
        "csrf_token": "html.selector",
        "cookies.session": "headers.set-cookie"
      }
    }
  ],
  "finalExtracted": {
    "access_token": "final_token",
    "cookies": {"session": "final_session"}
  },
  "finalExpiresAt": "2025-12-04T13:00:00Z",
  "success": true
}
```

## 6. 安全设计

### 6.1 敏感数据处理

#### 日志脱敏

```python
SENSITIVE_KEYS = {"password", "secret", "token", "key", "credential"}

def log_sensitive_payload(payload):
    if LOG_LEVEL == "DEBUG" and LOG_SENSITIVE_DEBUG == "1":
        logger.debug(f"Login payload: {payload}")  # 原文
    else:
        masked = {
            k: "***" if any(s in k.lower() for s in SENSITIVE_KEYS) else v
            for k, v in payload.items()
        }
        logger.debug(f"Login payload (masked): {masked}")
```

#### 存储策略

- `autoLoginEnabled=false`：不持久化 `loginPayload`，每次登录需手动提供
- `autoLoginEnabled=true`：加密存储（未来可集成密钥管理服务）

### 6.2 失效管理

#### 自动失效

登录失败时自动标记：

```python
await self.client.secret.update(
    where={"id": secret_id},
    data={
        "isValid": False,
        "status": "invalid",
        "invalidNotified": False,
        "lastLoginStatus": "failed",
        "lastLoginError": str(e)
    }
)
```

#### 通知闭环

```
[检测失效] -> [标记 isValid=false, invalidNotified=false]
    -> [发送告警] -> [标记 invalidNotified=true]
    -> [防止重复通知]
```

## 7. 扩展性设计

### 7.1 新协议支持

添加新认证协议只需：

1. 在 `auth_schemas.yaml` 添加默认配置
2. 更新 `AuthType` 枚举
3. 在 `client_helper.py` 添加专用方法（如 SMTP）

### 7.2 自定义解析器

响应解析支持插件式扩展：

```python
# 未来可支持
"responseMapping": {
  "custom": {
    "handler": "my_module.custom_parser",
    "config": {...}
  }
}
```

### 7.3 中间件集成

客户端支持注入自定义逻辑：

```python
class AuthClient:
    def __init__(self, ..., middlewares=None):
        self.middlewares = middlewares or []
    
    def request(self, method, url, **kwargs):
        for mw in self.middlewares:
            kwargs = mw.before_request(kwargs)
        
        response = self.session.request(method, url, **kwargs)
        
        for mw in self.middlewares:
            response = mw.after_response(response)
        
        return response
```

## 8. 性能优化

### 8.1 并发执行

异步客户端支持高并发：

```python
async with AsyncAuthClient(...) as client:
    tasks = [
        client.get(f"/api/resource/{i}")
        for i in range(100)
    ]
    results = await asyncio.gather(*tasks)
```

### 8.2 连接池复用

```python
# 复用 httpx 连接池
session = httpx.AsyncClient(
    limits=httpx.Limits(max_keepalive_connections=20)
)
client = AsyncAuthClient(..., session=session)
```

### 8.3 缓存策略

未来可在服务层添加 Token 缓存：

```python
@lru_cache(maxsize=1000)
def get_valid_token(secret_id: str) -> Optional[str]:
    secret = get_secret(secret_id)
    if secret.expiresAt > datetime.now(timezone.utc):
        return secret.data.get("access_token")
    return None
```

## 9. 测试策略

### 9.1 单元测试

核心引擎完全独立，易于测试：

```python
def test_auth_flow_executor():
    executor = AuthFlowExecutor(
        auth_method_config={"loginFlow": {...}},
        login_payload={"username": "test", "password": "pass"}
    )
    
    gen = executor.execute()
    req_cfg = next(gen)
    
    # 模拟响应
    mock_response = ResponseData(
        status_code=200,
        json_data={"access_token": "abc123"},
        ...
    )
    
    try:
        gen.send(mock_response)
    except StopIteration as e:
        result = e.value
        assert result.success
        assert "access_token" in result.finalExtracted
```

### 9.2 集成测试

使用 `httpx.MockTransport` 模拟 HTTP 请求：

```python
def mock_handler(request):
    if request.url.path == "/login":
        return httpx.Response(200, json={"token": "test"})
    return httpx.Response(404)

client = AuthClient(..., session=httpx.Client(transport=httpx.MockTransport(mock_handler)))
result = client.login()
```

## 10. 最佳实践

### 10.1 配置管理

- 使用环境变量管理敏感配置（`DATABASE_URL`, `AUTH_API_KEY`）
- 通过 `auth_schemas.yaml` 提供默认配置模板
- 在数据库中仅存储差异化配置

### 10.2 错误处理

- 登录失败自动重试（指数退避）
- Token 过期前自动刷新（`refreshBeforeSeconds`）
- 失败后降级到备用认证方式

### 10.3 监控告警

- 定时检查 `Secret.expiresAt` 并提前刷新
- 监控 `lastLoginStatus` 识别失败模式
- 追踪 `authDataSources` 定位配置问题

## 11. 未来规划

### 11.1 已规划功能

- [ ] 支持 OAuth2 PKCE 流程
- [ ] 集成密钥管理服务（KMS）
- [ ] 支持 mTLS 双向认证
- [ ] 提供 Web UI 配置界面

### 11.2 性能优化

- [ ] 实现 Token 缓存层
- [ ] 支持批量刷新操作
- [ ] 添加请求去重机制

### 11.3 生态集成

- [ ] 提供 Terraform Provider
- [ ] 支持 Kubernetes Operator
- [ ] 集成主流 API 网关

---

**文档版本**: v1.0  
**最后更新**: 2025-12-04  
**维护者**: turbo-agent-auth 团队
