Files
beaver_project/app-instance/backend-old/鉴权.md

29 KiB
Raw Blame History

鉴权方案设计

本文用于明确当前 nanobot-backend 后续要落地的鉴权和配置边界,重点覆盖:

  1. 一个前端管理多个 backend 的注册与身份模型
  2. backend 调 A2A / MCP 时的统一鉴权方式
  3. Outlook 外置 MCP 的权限校验与凭据读取方式
  4. 当前仓库与目标方案之间的差距
  5. 第一阶段可落地的 JSON 版 OAuth AuthZ Service 设计

本文按 2026-03-11 的需求收敛,不采用“一个 backend 多个 sandbox”的模型。当前结论是

  • 一个 backend 就是一个独立的 agent runtime也是一个独立的安全主体
  • 一个前端可以管理多个 backend
  • 当前先按一对一模拟实现,但数据模型和注册流程要为多个 backend 预留

1. 结论先行

最终要落成的不是“模型拿着 Outlook 账号密码调用工具”,而是下面这条链路:

  1. 用户在前端管理界面配置 Outlook 账号密码
  2. 前端把配置保存到独立的 AuthZ Service
  3. backend 自己只持有 backend_id 和自己的 OAuth client 身份
  4. backend 调 Outlook MCP 时先向 AuthZ Service 的 token endpoint 申请 access token再带 token 调用,不带账号密码
  5. Outlook MCP 校验 token 和权限
  6. Outlook MCP 再去 AuthZ Service 读取该 backend 的 Outlook 配置
  7. Outlook MCP 用取回的配置去执行真实 Outlook 操作
  8. 模型只能看到工具和结果,不能看到账号密码

这套方案的关键点有三个:

  • backend_id 是主体标识,不是凭证
  • 真正的鉴权依赖 AuthZ Service 作为 OAuth Authorization Server 签发的 access token
  • Outlook 凭据和 backend access token 是两套不同数据,不能混用

2. 设计目标

2.1 必须满足

  1. backend 必须先完成注册,注册后才能获得自己的身份
  2. A2A 和 MCP 都要能识别“当前是谁在调我”
  3. list_toolscall_tool 都必须鉴权
  4. Outlook 账号密码不进入模型上下文,不进入 prompt不作为工具参数传给模型
  5. 前端需要能查看当前配置状态,但默认只能看到脱敏后的敏感字段
  6. 当前阶段先允许用 JSON 做 AuthZ Service 存储

2.2 当前阶段不做

  1. 一个 backend 下再切多个 sandbox
  2. 复杂多租户组织、团队、成员模型
  3. 完整的密钥托管系统
  4. OAuth/OIDC 全套企业级接入

3. 主体与信任边界

3.1 主体定义

本方案只有一个核心安全主体:

  • backend

换句话说:

  • 一个 backend = 一个独立 agent runtime
  • 一个 backend = 一个独立权限实体
  • 一个 backend = 一份独立的 Outlook 配置归属

3.2 角色划分

Frontend

负责:

  • 管理 backend 注册
  • 管理 backend 的 Outlook 配置
  • 查询 backend 状态、权限状态、配置状态

不负责:

  • 直接决定工具是否可调用
  • 自己保存 backend 的长期信任根

Backend

负责:

  • 跑 agent
  • 调 A2A / MCP
  • 持有自己的注册身份
  • 调用前向 AuthZ Service 申请短期 token

不负责:

  • 保存 Outlook 密码
  • 本地决定 Outlook 权限是否放行

AuthZ Service

负责:

  • backend 注册中心
  • backend 凭证校验
  • 权限配置
  • settings 存储
  • 作为 OAuth Authorization Server 签发 access token
  • 提供 OAuth metadata / JWKS / introspection
  • 给 MCP/A2A 做 token 校验或 introspection

External Outlook MCP

负责:

  • 校验 backend 身份
  • 判断 backend 是否开通 Outlook MCP 和具体工具权限
  • AuthZ Service 获取 Outlook 配置
  • 以服务端身份完成 Outlook 调用

3.3 总体架构图

flowchart LR
    UI[Frontend]
    AZ[AuthZ Service<br/>JSON storage]
    BE[Backend<br/>Agent Runtime]
    A2A[A2A Remote Agent]
    MCP[External Outlook MCP]
    O365[Outlook / Exchange]

    UI -->|register backend| AZ
    UI -->|save masked settings| AZ
    UI -->|view backend status| AZ
    UI -->|chat / manage| BE

    BE -->|request short-lived token| AZ
    BE -->|A2A request + Bearer token| A2A
    BE -->|list_tools / call_tool + Bearer token| MCP

    A2A -->|verify or introspect token| AZ
    MCP -->|verify or introspect token| AZ
    MCP -->|load backend outlook settings| AZ
    MCP -->|execute mail/calendar action| O365

3.4 OAuth 角色映射

为避免后续扩多个 backend / 多个受保护 MCP / A2A 时重新设计,建议现在就按标准 OAuth 角色建模:

  1. AuthZ Service

    • OAuth Authorization Server
    • 同时也是业务配置源
  2. backend

    • OAuth client
    • 当前阶段是 confidential client
  3. Outlook MCP

    • Resource Server
  4. 受保护的 A2A agent

    • Resource Server

这样做的好处是:

  1. MCP 和 A2A 共用同一套 access token 模型
  2. 后续可以平滑增加更多 backend
  3. 后续可以平滑增加更多受保护资源服务
  4. 公开第三方服务仍可保留 auth_mode=none

3.5 推荐服务结构

如果单独做一个 authz-service,建议结构至少拆成下面这样:

authz-service/
├── app/
│   ├── main.py
│   ├── api/
│   │   ├── backends.py
│   │   ├── permissions.py
│   │   ├── settings.py
│   │   └── oauth.py
│   ├── core/
│   │   ├── auth.py
│   │   ├── oauth_tokens.py
│   │   ├── scopes.py
│   │   └── settings_access.py
│   ├── storage/
│   │   ├── json_store.py
│   │   ├── backends_repo.py
│   │   ├── credentials_repo.py
│   │   ├── permissions_repo.py
│   │   └── settings_repo.py
│   └── models/
│       ├── backend.py
│       ├── oauth.py
│       ├── permission.py
│       └── settings.py
└── data/
    ├── backends.json
    ├── backend_credentials.json
    ├── permissions.json
    └── settings.json

对应职责:

  1. api/backends.py

    • backend 注册、查询、禁用、启用
  2. api/oauth.py

    • /.well-known/oauth-authorization-server
    • /.well-known/jwks.json
    • /oauth/token
    • /oauth/introspect
  3. core/oauth_tokens.py

    • 生成 JWT access token
    • 校验 scope / audience
  4. storage/*.py

    • 对 JSON 文件做原子读写

3.6 backend 侧配置结构

backend 侧建议只保留身份与路由配置,不保留 Outlook 业务凭据。

推荐最小结构:

{
  "backend_identity": {
    "backend_id": "backend_local_001",
    "client_id": "backend_local_001",
    "client_secret": "generated-secret"
  },
  "authz": {
    "base_url": "http://127.0.0.1:19090"
  },
  "a2a_targets": {
    "planner": {
      "base_url": "https://planner.example.com",
      "auth_mode": "oauth_backend_token"
    },
    "public-search-agent": {
      "base_url": "https://public.example.com",
      "auth_mode": "none"
    }
  },
  "mcp_targets": {
    "outlook": {
      "url": "https://mcp.example.com/outlook",
      "auth_mode": "oauth_backend_token"
    },
    "public-fetcher": {
      "url": "https://mcp.example.com/public",
      "auth_mode": "none"
    }
  }
}

这样做的边界是:

  1. backend 知道“去哪里申请 token”
  2. backend 知道“某个目标该不该带 OAuth token”
  3. backend 不知道 Outlook 账号密码

4. 当前仓库现状与缺口

当前仓库已经有 A2A、MCP、Outlook 集成,但鉴权边界还不满足目标方案。

4.1 A2A 现状

当前 A2A 客户端会从 agent.auth_env 指定的环境变量读取 token再塞进 Authorization 请求头。

相关代码:

  • nanobot/a2a/client.py

当前问题:

  1. token 是静态环境变量,不是为当前 backend 动态签发
  2. token 与 backend 注册体系没有绑定
  3. 无法表达更细的 audience 和 scope

4.2 MCP 现状

当前 MCP 连接方式支持:

  • stdio
  • HTTP url + headers

相关代码:

  • nanobot/config/schema.py
  • nanobot/agent/tools/mcp.py

当前问题:

  1. MCP 连接建立后,工具会被直接注册到本地 registry
  2. list_tools() 阶段没有按 backend 身份做鉴权
  3. call_tool() 阶段没有按 backend 身份做细粒度校验
  4. HTTP 模式也只是静态 headers,不是按每次调用动态签 token

4.3 Outlook 现状

当前 Web 侧 Outlook 集成会把配置写到 workspace 对应的外部状态文件里,并自动把 Outlook MCP 注册为本地 MCP server。

相关代码:

  • nanobot/web/outlook.py
  • nanobot/web/server.py

当前问题:

  1. Outlook 账号密码当前仍然是 workspace 级保存
  2. Outlook 配置与 backend 注册体系还没有打通
  3. Outlook MCP 的注册方式仍偏向“backend 本地接入工具”,而不是“外置服务按 backend 鉴权”

4.4 Web 接口现状

当前 Web 登录接口存在,但大部分管理接口没有统一接入鉴权依赖。

相关代码:

  • nanobot/web/server.py

当前问题:

  1. 登录后只是在内存里保存一个 Web bearer token
  2. 大部分管理路由没有显式要求该 token
  3. 这套 Web 登录还不是 backend 注册体系的一部分

5. 目标模型

5.1 backend 作为唯一安全主体

本方案不再引入额外的 sandbox_id 概念,而是直接使用:

  • backend_id

它同时承担:

  • 权限主体标识
  • Outlook 配置归属标识
  • A2A / MCP 调用身份的业务主键

5.2 backend 标识与凭证分离

必须区分这三类字段:

  1. backend_id

    • 稳定主键
    • 可被前端展示
    • 可用于日志
  2. client_id

    • backend 向 AuthZ Service 认证时使用
    • 可以等于 backend_id
  3. client_secret

    • backend 的长期凭证
    • 只在注册成功返回时展示一次
    • AuthZ Service 只保存 hash

5.3 token 作为实际调用凭证

backend 在调用 A2A / MCP 前,不直接拿 client_secret 调目标服务,而是先向 AuthZ Service 的 OAuth token endpoint 申请短期 access token。

该 token 至少包含:

  • sub: backend:<backend_id>
  • backend_id
  • aud: mcp:outlooka2a:<agent_id>
  • scp: scope 列表
  • iat
  • exp
  • jti

推荐:

  • 默认过期时间 5 分钟到 15 分钟
  • 面向单个目标服务签发
  • 面向单次或短时窗口调用复用

5.4 OAuth 模型

当前阶段推荐直接按 OAuth 做,而不是再造一套与 OAuth 相似但不兼容的 token 系统。

建议第一版使用:

  1. grant type

    • client_credentials
  2. client 类型

    • confidential client
  3. token 类型

    • Bearer access token
  4. token 格式

    • 优先 JWT
    • 必要时辅以 introspection

原因:

  1. backend 到 MCP / A2A 是标准机器到机器调用
  2. 多 backend 扩展时不需要换模型
  3. 多 resource server 扩展时不需要换模型
  4. 后续若要接入标准 SDK、网关或审计系统更容易

6. 数据模型

当前阶段使用 JSON 做持久化,建议至少拆成 4 份文件,避免把身份、权限、配置和密钥混在一起。

6.1 backends.json

保存 backend 主记录。

{
  "backends": [
    {
      "backend_id": "backend_local_001",
      "name": "Local Backend",
      "base_url": "http://127.0.0.1:18080",
      "status": "active",
      "created_at": "2026-03-11T10:00:00Z",
      "updated_at": "2026-03-11T10:00:00Z"
    }
  ]
}

6.2 backend_credentials.json

保存 backend 长期凭证的 hash不存明文。

{
  "credentials": [
    {
      "backend_id": "backend_local_001",
      "client_id": "backend_local_001",
      "client_secret_hash": "hashed-secret",
      "created_at": "2026-03-11T10:00:00Z",
      "rotated_at": null
    }
  ]
}

6.3 permissions.json

保存 backend 对 A2A / MCP 的能力授权。

{
  "permissions": {
    "backend_local_001": {
      "mcp": {
        "outlook": {
          "enabled": true,
          "tools": ["list_mail", "read_mail", "send_mail"]
        }
      },
      "a2a": {
        "enabled": true,
        "agents": ["planner", "calendar-agent"]
      }
    }
  }
}

6.4 settings.json

保存业务配置。当前阶段可以临时把 Outlook 配置放在这里,但要明确这是过渡态。

{
  "settings": {
    "backend_local_001": {
      "outlook": {
        "configured": true,
        "email": "user@corp.com",
        "username": "user",
        "domain": "corp",
        "service_endpoint": "https://mail.example.com/EWS/Exchange.asmx",
        "password": "plain-text-for-now",
        "updated_at": "2026-03-11T10:00:00Z"
      }
    }
  }
}

补充约束:

  1. 前端查询配置时,默认不能返回明文密码
  2. 管理页面只回显非敏感字段和“是否已配置”
  3. 后续应迁移为:
    • settings.json 保存非敏感配置
    • secrets.json 或外部 secret store 保存敏感值

6.5 JSON 存储约束

既然当前阶段用 JSON 做存储,就必须明确写入约束,否则很容易因为并发更新把文件写坏。

建议最少满足:

  1. 所有写操作先写临时文件,再原子替换
  2. 同一类文件写入时加进程内锁
  3. 文件格式损坏时要能快速回滚或人工修复
  4. backends.jsonpermissions.jsonsettings.jsonbackend_credentials.json 不要混写
  5. 每次写入都刷新 updated_at

7. 注册流程

backend 注册不是创建聊天账号,而是把一个 backend 纳入信任体系。

7.1 注册步骤

  1. 用户在前端注册页提交账号信息
  2. 前端把用户信息发给 AuthZ Service
  3. AuthZ Service 在注册流程中为当前 backend 生成:
    • backend_id
    • client_id
    • client_secret
  4. AuthZ Service 同时创建一条 OAuth client 记录,并记录用户信息与 backend 归属
  5. 前端把这组 backend 身份信息交给对应 backend 保存
  6. backend 后续通过 client_id + client_secretAuthZ Service/oauth/token 申请 access token
  7. 用户后续不再进入独立的 backend 列表页做这件事

7.2 注册时序图

sequenceDiagram
    participant UI as Frontend
    participant AZ as AuthZ Service
    participant BE as Backend

    UI->>AZ: POST /oauth/register
    AZ-->>UI: user + backend_id + client_id + client_secret
    UI->>AZ: POST /backends/{id}/permissions
    AZ-->>UI: ok
    UI->>BE: 保存 backend identity

7.3 注册后 backend 本地至少需要持有

  1. backend_id
  2. client_id
  3. client_secret
  4. authz_base_url

当前阶段建议:

  • backend 把这组配置写到本地配置文件或环境变量
  • 前端不再反复下发明文 secret

7.4 backend 凭证轮换与禁用

虽然第一阶段可以不先做完整 UI但模型必须预留以下动作

  1. 轮换 client_secret
  2. 禁用 backend
  3. 重新启用 backend

最少行为应定义为:

  1. backend 被禁用后,/oauth/token 不再签发新 token
  2. 已签发 token 到期后自然失效
  3. backend 被重新启用后才恢复签发

8. Outlook 配置流程

8.1 目标

用户在前端界面录入 Outlook 配置后:

  1. 配置进入 AuthZ Service
  2. backend 本地不保存账号密码
  3. Outlook MCP 需要时再向 AuthZ Service 读取

8.2 流程图

sequenceDiagram
    participant UI as Frontend
    participant AZ as AuthZ Service

    UI->>AZ: POST /backends/{id}/settings/outlook
    Note over UI,AZ: email / username / password / endpoint
    AZ-->>UI: saved
    UI->>AZ: GET /backends/{id}/settings/outlook
    AZ-->>UI: masked config

8.3 前端回显规则

MCP 详情页可以展示:

  • email
  • username
  • domain
  • service_endpoint
  • configured
  • updated_at

MCP 详情页默认不直接展示:

  • 明文 password

推荐返回格式:

{
  "configured": true,
  "email": "user@corp.com",
  "username": "user",
  "domain": "corp",
  "service_endpoint": "https://mail.example.com/EWS/Exchange.asmx",
  "password_masked": true,
  "updated_at": "2026-03-11T10:00:00Z"
}

9. A2A 鉴权方案

9.1 目标

A2A 侧要做到:

  1. backend 身份可识别
  2. 远端 agent 可以判断调用方是谁
  3. 远端 agent 可以按 backend 控制是否允许访问

9.2 公开第三方 A2A 兼容策略

不是所有第三方 A2A 都必须接入你们自己的 OAuth。

建议对 A2A 目标增加 auth_mode

  1. none

    • 公开第三方 agent
    • backend 可直接调用
  2. oauth_backend_token

    • 你们自己的受保护 A2A agent
    • backend 先向 AuthZ Service 申请 access token 再调用
  3. static_secret

    • 某些第三方需要固定 API key 或固定 bearer token

平台侧即便 auth_mode=none,也仍建议保留:

  1. host allowlist
  2. enable/disable 开关
  3. 超时和并发限制
  4. 审计日志

9.3 当前方案与未来方案对比

当前:

  • backend 通过静态环境变量向 A2A 附带 Bearer Token

目标:

  1. backend 在每次调用 A2A 前,向 AuthZ Service 请求短期 token
  2. token 的 aud 绑定具体 A2A 目标
  3. 远端 A2A 服务校验 token 或调用 introspection
  4. 未授权时返回明确 401/403

9.4 推荐 token claim

{
  "sub": "backend:backend_local_001",
  "backend_id": "backend_local_001",
  "aud": "a2a:planner",
  "scp": ["run_task"],
  "iat": 1773209700,
  "exp": 1773210000,
  "jti": "uuid"
}

9.5 A2A agent card 暴露策略

建议分两层:

  1. 公共 card

    • 只暴露最小信息
    • 不承诺所有内部能力都可见
  2. 鉴权后的能力视图

    • 由服务端根据 backend 权限决定是否允许调用

实现上可以简化为:

  • card 可公开
  • 真正调用时严格校验 run_task 权限

10. MCP 鉴权方案

10.1 结论

涉及 backend 身份鉴权的外置 MCP优先统一走 HTTP transport不再依赖本地 stdio 进程边界。

原因:

  1. stdio 更像本机受信任进程通信
  2. backend 身份、token、远端服务审计更适合 HTTP
  3. list_toolscall_tool 都要做按 backend 的动态判断

10.2 MCP 的认证模式

不是所有第三方 MCP 都必须接入你们的 OAuth。

建议 MCP 目标也增加 auth_mode

  1. none

    • 完全公开的第三方 MCP
    • 不要求 backend token
  2. oauth_backend_token

    • 你们自己的受保护 MCP
    • backend 必须先拿 access token 再调用
  3. static_secret

    • 某些第三方 HTTP MCP 需要固定 token 或 API key

其中:

  1. Outlook MCP 应归类为 oauth_backend_token
  2. 公开第三方 MCP 不应被这个方案破坏
  3. 平台侧仍建议保留 host allowlist 和 enable/disable

10.3 list_tools 规则

list_tools 必须鉴权,不能再视为“只是列功能,不敏感”。

建议语义:

  1. 未认证:401
  2. backend 未开通该 MCP403
  3. backend 开通 MCP 但没有任何工具:返回空数组
  4. backend 已开通且有部分工具权限:只返回允许的工具

10.4 call_tool 规则

建议语义:

  1. 未认证:401
  2. token audience 错误:403
  3. backend 未开通该工具:403
  4. backend 已开通但 Outlook 未配置:400
  5. 上游 Outlook 调用失败:按业务错误返回

10.5 Outlook MCP 调用时序图

sequenceDiagram
    participant BE as Backend
    participant AZ as AuthZ Service
    participant MCP as Outlook MCP
    participant O365 as Outlook / Exchange

    BE->>AZ: POST /oauth/token (aud=mcp:outlook)
    AZ-->>BE: access_token

    BE->>MCP: list_tools / call_tool + Bearer token
    MCP->>AZ: introspect token
    AZ-->>MCP: backend_id + scopes valid

    MCP->>AZ: GET /internal/backends/{id}/settings/outlook
    AZ-->>MCP: email / username / password / endpoint

    MCP->>O365: execute actual action
    O365-->>MCP: result
    MCP-->>BE: tool result

10.6 Outlook MCP 需要做的事

  1. 从 token 中识别 backend_id
  2. 查询该 backend 是否启用 outlook MCP
  3. 查询该 backend 是否允许当前 tool_name
  4. 查询该 backend 是否已配置 Outlook
  5. AuthZ Service 取回配置
  6. 执行工具
  7. 返回结果,不回传密钥

10.7 MCP 工具缓存与失效

由于当前仓库会把已连接 MCP 的工具注册进本地 registry因此即便未来一个 backend 只代表一个主体,也要定义缓存失效规则。

建议:

  1. backend 启动后可缓存自己有权访问的工具列表
  2. permissions.outlook.tools 变更时,要求 backend 主动 reload MCP
  3. 当 Outlook 配置从“已配置”变为“未配置”时,call_tool 不能依赖旧缓存继续执行
  4. list_tools 的最终结果以 MCP 服务端鉴权结果为准backend 本地缓存只作为性能优化

11. AuthZ Service API 草案

当前阶段最小接口集如下。

11.0 OAuth 基础端点

建议第一阶段就预留标准 OAuth 元数据端点:

  1. GET /.well-known/oauth-authorization-server
  2. GET /.well-known/jwks.json
  3. POST /oauth/token
  4. POST /oauth/introspect

这样后续 MCP / A2A resource server 不需要绑定你们的私有业务接口格式。

11.1 backend 注册

POST /backends/register

请求:

{
  "name": "Local Backend",
  "base_url": "http://127.0.0.1:18080"
}

响应:

{
  "backend_id": "backend_local_001",
  "client_id": "backend_local_001",
  "client_secret": "generated-secret",
  "created_at": "2026-03-11T10:00:00Z"
}

11.2 查询 backend

GET /backends/{backend_id}

响应:

{
  "backend_id": "backend_local_001",
  "name": "Local Backend",
  "base_url": "http://127.0.0.1:18080",
  "status": "active"
}

11.3 更新权限

POST /backends/{backend_id}/permissions

请求:

{
  "mcp": {
    "outlook": {
      "enabled": true,
      "tools": ["list_mail", "read_mail", "send_mail"]
    }
  },
  "a2a": {
    "enabled": true,
    "agents": ["planner", "calendar-agent"]
  }
}

11.4 查询权限

GET /backends/{backend_id}/permissions

11.5 保存 Outlook 配置

POST /backends/{backend_id}/settings/outlook

请求:

{
  "email": "user@corp.com",
  "username": "user",
  "domain": "corp",
  "service_endpoint": "https://mail.example.com/EWS/Exchange.asmx",
  "password": "plain-text-for-now"
}

11.6 读取 Outlook 配置

对前端:

  • GET /backends/{backend_id}/settings/outlook

默认返回脱敏字段。

对 MCP 内部:

  • GET /internal/backends/{backend_id}/settings/outlook

只允许受信任服务访问,可返回完整配置。

11.7 OAuth token endpoint

POST /oauth/token

请求:

{
  "grant_type": "client_credentials",
  "client_id": "backend_local_001",
  "client_secret": "generated-secret",
  "aud": "mcp:outlook",
  "scopes": ["list_tools", "tool:read_mail"]
}

响应:

{
  "access_token": "jwt-or-signed-token",
  "token_type": "bearer",
  "expires_in": 300
}

说明:

  1. 当前阶段可以允许 aud 作为自定义字段
  2. 后续若采用更标准实现,可改成 resource 或约定好的 scope 模型

11.8 OAuth introspection endpoint

POST /oauth/introspect

请求:

{
  "token": "jwt-or-signed-token"
}

响应:

{
  "active": true,
  "backend_id": "backend_local_001",
  "aud": "mcp:outlook",
  "scp": ["list_tools", "tool:read_mail"],
  "exp": 1773210000
}

11.9 建议追加但可后做的接口

以下接口不是第一天必须做完,但建议作为后台/运维接口预留,不直接暴露成用户侧 backend 列表页:

  1. POST /backends/{backend_id}/rotate-secret
  2. POST /backends/{backend_id}/disable
  3. POST /backends/{backend_id}/enable
  4. GET /backends
  5. GET /audit/logs
  6. POST /oauth/register

12. 前端页面要求

当前阶段前端至少需要 3 个页面,不再给用户单独暴露 backend 列表页。

12.1 登录页

展示与交互:

  • 用户名 / 密码登录
  • 登录成功后进入主界面或 MCP 管理页

12.2 注册页

展示与交互:

  • 用户名
  • 邮箱
  • 密码
  • 注册成功后自动触发 AuthZ Service 里的用户信息记录与 backend/sandbox 通行证初始化
  • 注册成功后把 backend identity 保存到当前 backend

12.3 MCP 管理页 / MCP 详情页

展示与编辑:

  • email
  • username
  • domain
  • service_endpoint
  • password
  • configured
  • updated_at

交互要求:

  1. 敏感信息在对应 MCP 的详情页里保存
  2. 保存请求直接发到 AuthZ Service
  3. 保存后刷新状态
  4. 默认展示脱敏后的配置状态
  5. 重新编辑时允许覆盖旧密码

13. 与当前仓库的改造边界

本文先定方案,不直接改代码,但需要明确后续改造点。

13.1 backend 不再本地保存 Outlook 密码

当前:

  • nanobot/web/outlook.py 会把 Outlook 配置保存到 workspace 对应文件

目标:

  • nanobot/web/outlook.py 只负责调用 AuthZ Service
  • backend 本地只保留“是否已配置”的只读状态缓存,必要时甚至不缓存

13.2 Outlook MCP 改为外置 HTTP MCP

当前:

  • Outlook MCP 更偏向“本地注册 MCP server”

目标:

  • Outlook MCP 是独立外置服务
  • backend 调它时带 token
  • 它自己去 AuthZ Service 拉 Outlook 配置

13.3 A2A 统一接入 backend identity

当前:

  • A2A 用静态 env token

目标:

  • A2A 与 MCP 统一使用 backend 短期 token

13.4 Web 管理接口需要统一鉴权

当前:

  • Web 登录存在
  • 但管理路由没有统一接入鉴权依赖

目标:

  • 前端所有管理行为先经过 Web 登录
  • backend 的注册与管理接口再与 AuthZ Service 对接

14. 第一阶段落地顺序

建议按下面顺序推进,避免一次改散。

阶段 1AuthZ Service

先完成:

  1. JSON 存储层
  2. backend 注册接口
  3. 权限接口
  4. Outlook settings 接口
  5. OAuth metadata / JWKS / token / introspect 接口

阶段 2做前端管理页

先完成:

  1. backend 注册
  2. Outlook 配置
  3. Outlook 配置状态查看

阶段 3改 backend

先完成:

  1. backend 接入自己的 backend_id/client_secret
  2. 调 Outlook MCP 时自动申请 token
  3. Outlook Web 配置接口改为转发到 AuthZ Service

阶段 4改 Outlook MCP

先完成:

  1. token 校验
  2. 权限校验
  3. AuthZ Service 拉 Outlook 配置
  4. list_tools / call_tool 分别按 backend 鉴权

15. 风险与补缺

这部分是本方案里容易漏掉、但必须提前写清楚的点。

15.1 明文密码只允许作为阶段性过渡

当前阶段为了快速模拟,可以先把 Outlook 密码明文存在 JSON 中,但必须明确:

  1. 这不是最终方案
  2. 文档和代码里都要标记为过渡态
  3. 后续至少要改成加密存储或外部 secret store

如果业务坚持“前端管理界面必须能看见明文密码”,建议额外加一道控制:

  1. 只有高权限操作者才能 reveal
  2. reveal 前要求重新确认身份
  3. 每次 reveal 写审计日志

15.2 token 不能只带 backend_id

如果 token 只是一个可猜的 backend_id,那不是鉴权。

至少要满足:

  1. 可校验签名或可 introspection
  2. 有过期时间
  3. 有 audience
  4. 有 scope

15.3 list_tools 也属于敏感接口

不要把 list_tools 当作无害操作。

原因:

  1. 工具名本身可能暴露系统能力
  2. 工具参数 schema 可能透露内部实现
  3. 有些工具枚举本身就是权限信息

15.4 backend 与前端身份不能混用

前端登录态和 backend 调用态不是一回事。

必须区分:

  1. 前端用户登录 token
  2. backend 调 A2A / MCP 的 backend token

15.5 审计日志建议第一阶段就留口子

建议 AuthZ Service 和 Outlook MCP 至少记录:

  1. backend_id
  2. 调用目标
  3. tool_name
  4. 结果状态
  5. 失败原因
  6. 时间戳

但不要把密码、token 明文写入日志。

15.6 配置删除与失效要有一致性

当 Outlook 配置被移除时,建议同时做到:

  1. settings.outlook.configured = false
  2. permissions.mcp.outlook.enabled 可选择自动关闭或显式保留
  3. 后续 call_tool 必须返回“未配置”

15.7 MCP 与 AuthZ 的内部信任也要单独设计

不要让 Outlook MCP 匿名读取 AuthZ Service 的内部配置接口。

至少要满足:

  1. Outlook MCP 自己也有一套服务端凭证
  2. GET /internal/backends/{id}/settings/outlook 只允许受信任服务调用
  3. backend 自己不能直接拿 backend token 访问 internal 明文配置接口

16. 参考实现建议

当前阶段建议保守实现,不追求复杂化。

16.1 AuthZ Service 技术选型

建议:

  • FastAPI
  • JSON 文件存储
  • 进程内文件锁或原子写

16.2 token 实现方式

二选一都可:

  1. JWT
  2. 自定义签名 token + introspection

当前阶段更省事的方式是:

  • 先做带签名的短期 JWT
  • MCP / A2A 无法本地验签时再走 introspection

16.3 Outlook MCP 访问 AuthZ Service

推荐:

  • Outlook MCP 使用内部服务凭证访问 AuthZ Service 的 internal API
  • 不直接复用 backend token 读内部明文配置

17. 一句话边界总结

整个方案最终要保证的边界就是:

  • 前端负责配置
  • AuthZ Service 负责存储和签发身份
  • backend 负责拿身份去调服务
  • Outlook MCP 负责验证身份并读取配置
  • 模型只负责调用工具,不接触账号密码

18. 外部规范参考

以下规范只作为设计参考,具体实现仍以本仓库实际边界为准:

  1. A2A Specification
  2. A2A Enterprise-Ready Topics
  3. Model Context Protocol Authorization
  4. Model Context Protocol OAuth Client Credentials Extension