Chorus 将每个请求认证为统一的 `AuthContext`，再根据细粒度的权限集合管控智能体能做什么。本页
说明各种认证方式、智能体 API 密钥的约定，以及同时驱动 MCP 工具可见性与 REST 访问的权限模型。

可引用的稳定锚点：[`#统一的-authcontext`](#统一的-authcontext)、
[`#解析级联`](#解析级联)、[`#agent-api-key`](#agent-api-key)、
[`#权限模型`](#权限模型)、[`#角色预设`](#角色预设)、
[`#自定义权限与有效集合`](#自定义权限与有效集合)。

## 统一的 AuthContext

每个已认证的请求都会解析为三种上下文类型之一。多租户通过 `companyUuid` 强制隔离：所有数据访问都
以它为范围，只有 Super Admin 可以跨工作区操作。

| 上下文类型 | `type` | 关键字段 | 由谁产生 |
| --- | --- | --- | --- |
| User | `user` | `companyUuid`、`actorUuid`、`email`、`name` | OIDC、Default Auth |
| Agent | `agent` | `companyUuid`、`actorUuid`、`roles[]`（预设选择器）、`permissions[]`（有效集合）、`agentName`、`ownerUuid` | API 密钥 |
| Super Admin | `super_admin` | `email`（无 `companyUuid`） | Super Admin 会话 |

智能体的授权完全来自它的 `permissions[]`，即扁平的有效权限集合。`roles[]` 字段只是预设选择器，
并不是授权来源。

## 解析级联

单一入口按优先级依次尝试每种方式，第一个成功即返回：

```text
1. Authorization: Bearer <token>
   ├─ cho_… 前缀   → API key 校验            → Agent 上下文
   ├─ RS*/ES* JWT   → OIDC token 校验          → User 上下文
   └─ HS256 JWT     → Chorus JWT 校验           → User 上下文
2. 会话 Cookie
   └─ user_session                         → User
3. OIDC Cookie（用于 SSE / EventSource，无 Authorization 头）
   └─ oidc_access_token cookie                → User 上下文
4. 无匹配 → 未认证
```

步骤 1 中的 token 按形态分类：`cho_` 前缀即 API 密钥；否则视为三段式 JWT：头部算法为非对称
（`RS*`/`ES*`）时为 OIDC token，为 `HS256` 时为 Chorus 自签名会话 token。

## 会话签名密钥

`NEXTAUTH_SECRET` 用于签名 Default Auth 的 `user_session` 和 Super Admin 的 `admin_session` JWT。
Super Admin 路由会单独校验 `admin_session`，与普通用户认证相互独立。
OIDC token 用身份提供方的密钥校验，智能体 API 密钥则与数据库中保存的哈希比对。
轮换 `NEXTAUTH_SECRET` 只会使 Chorus 自签名会话失效，不会使 API 密钥或 OIDC token 失效。

npm 启动器默认将生成的密钥保存在 `~/.chorus-data/.secret`，Docker 则使用 `/app/data/.secret`。
两者都会忽略环境变量中的已知公开占位值；若已保存的密钥是公开占位值，则拒绝启动。
直接启动应用时需自行提供密钥。0.18.1 升级、持久化与多副本设置参见
[Docker 会话密钥](/zh/guides/deploy-docker/#会话密钥nextauth_secret)。

## Agent API key

智能体在每个 MCP 和 REST 请求上用 bearer API 密钥认证：

```http
Authorization: Bearer cho_<random>
```

约定如下：

- **`cho_` 前缀。** 每个密钥以 `cho_` 开头，后接 base64url 编码的随机字节。解析级联正是靠这个
  前缀区分密钥与 JWT。
- **仅展示一次。** 原始密钥只在创建时返回一次。请当场复制；之后无法再取回。
- **以 SHA-256 哈希形式存储。** 数据库只存密钥的 SHA-256 哈希，从不保存原始值，因此即便数据库
  被导出也不会泄露可用凭据。
- **恒定时间比较。** 校验时对提交的 token 做哈希，再用 constant-time 比较两个哈希值，因此无论前
  缀匹配多少位，失败耗时都相同。
- **轮换、吊销、过期。** 密钥可被吊销，也可设置可选的过期时间；被吊销或过期的密钥校验失败。轮换
  时创建一个替代密钥并吊销旧密钥。权限挂在智能体上而非密钥上，因此轮换密钥不会改变智能体的
  权限；要更改权限，请编辑智能体。

:::caution[密钥仅展示一次]
创建后无法恢复原始密钥。请将其存入密钥管理服务，像密码一样对待。一旦泄露，请立即吊销并签发替代
密钥。
:::

创建、编辑、轮换、吊销密钥属于**设置**中的操作，而非 API 调用。凭据创建步骤见
[为智能体创建访问权限](/zh/guides/administrator-setup/)，编辑与吊销见
[管理智能体与 API 密钥](/zh/guides/manage-agents/)，将密钥接入智能体见
[连接智能体运行环境](/zh/guides/operator-onboarding/)。

## 其他认证方式

以下方式面向人类用户和平台管理员。这里只做概要说明，具体配置见运维指南。

### OIDC（用户）

面向人类用户的企业 SSO，由 Super Admin 按工作区配置（issuer、client ID、启用开关）。登录使用带
**PKCE** 的授权码流程，无需 client secret。身份提供方回调后，Chorus 按
`(companyUuid, oidcSub)` 查找或创建用户，并设置若干 HTTP-only cookie：

| Cookie | 用途 |
| --- | --- |
| `oidc_access_token` | 用于 API 调用的访问 token（约 1 小时） |
| `oidc_refresh_token` | 用于静默续期的刷新 token（约 30 天） |
| `oidc_client_id` | token 刷新时使用的 client ID |
| `oidc_issuer` | 用于 JWKS 发现的 issuer |

访问 token 依据身份提供方的 JWKS（带缓存）校验；Chorus 会在请求到达应用前，透明地续期
即将过期的访问 token。

### Default Auth（开发用）

为没有 OIDC 基础设施的开发与演示部署提供单个邮箱/密码登录。仅在同时设置 `DEFAULT_USER` 和
`DEFAULT_PASSWORD` 时启用：

```bash
DEFAULT_USER="dev@example.local"
DEFAULT_PASSWORD="change-me"
```

登录时 Chorus 会自动开通工作区与用户，并签发一个长期有效的自签名 JWT 放入 `user_session` cookie。
它没有刷新流程，过期后重新登录即可。请勿在生产环境启用 Default Auth。

### Super Admin

负责跨工作区的平台级管理（工作区管理、按工作区配置 OIDC）。通过环境变量配置一个邮箱和一个 **bcrypt**
密码哈希：

```bash
SUPER_ADMIN_EMAIL="admin@example.com"
SUPER_ADMIN_PASSWORD_HASH="$2b$10$…"   # bcrypt 哈希
```

登录成功后设置 `admin_session` cookie。Super Admin 上下文**没有 `companyUuid`**，因此不受工作区
范围限制，是唯一能跨租户读取数据的上下文。

## 权限模型

智能体授权由一组权限位构成。智能体持有的有效集合在每一层都决定着它能做什么。

### 5 × 3 矩阵

权限写作 `{resource}:{action}`。五个资源乘以三个动作，共 15 个可能的位：

| | `read` | `write` | `admin` |
| --- | --- | --- | --- |
| **`idea`** | 查看想法 | 创建 / 认领 / 释放 / 更新想法；运行细化轮次 | 关闭 / 删除想法 |
| **`proposal`** | 查看提案和草稿 | 创建 / 提交 / 拒绝 / 撤回；管理草稿；批量创建任务；管理任务 DAG；分配任务 | 批准 / 关闭提案 |
| **`document`** | 查看文档 | 创建 / 更新文档 | 删除文档 |
| **`task`** | 查看任务 | 认领 / 释放 / 提交 / 上报；自检验收标准 | 验收 / 重开 / 关闭 / 删除任务；标记验收标准 |
| **`project`** | 查看项目与项目组 | 创建 / 更新 / 删除项目与项目组；移动项目 | 保留位（由 `admin_agent` 授予，尚未管控） |

这些动作通常一起授予，但彼此**不会**相互隐含：授予 `task:admin` 并不隐含 `task:read` 或
`task:write`，每个位都要显式授予。

:::caution[`*:admin` 位是人类级别的]
`admin` 列涵盖通常需要人类审核者的动作：批准提案、验收任务、删除实体。授予某个 `*:admin`
位等于让智能体代替人类做出这一判断（且不区分作者：`task:admin` 可验收工作区内任意任务，而不仅是该智能体
做过的任务）。只把 admin 位授予那些有意自动化审批流程的智能体。
:::

### 角色预设

三个具名预设展开为 15 个位的固定子集。预设只是快捷方式，不是独立的授权机制。

| 预设 | 数量 | 展开的权限 |
| --- | :---: | --- |
| `developer_agent` | **6** | `*:read` + `task:write` |
| `pm_agent` | **10** | `*:read` + `idea:write` + `proposal:write` + `document:write` + `task:write` + `project:write` |
| `admin_agent` | **15** | 全部位（`*:read` + `*:write` + `*:admin`） |

展开后，各预设为：

```text
developer_agent (6):
  idea:read  proposal:read  document:read  project:read  task:read
  task:write

pm_agent (10):
  idea:read      idea:write
  proposal:read  proposal:write
  document:read  document:write
  task:read      task:write
  project:read   project:write

admin_agent (15):
  idea:read      idea:write      idea:admin
  proposal:read  proposal:write  proposal:admin
  document:read  document:write  document:admin
  task:read      task:write      task:admin
  project:read   project:write   project:admin
```

### 自定义权限与有效集合

在预设之外，智能体还可携带自定义权限位。**有效集合**是展开后的预设与自定义位的并集：

```text
effective = expand(preset) ∪ custom
```

顺序无关，它是纯粹的集合并集。常见形态：

- **只读审计员**：无预设，仅自定义 `*:read`，可查看一切但不能改动。
- **自验收开发者**：`developer_agent` + `task:admin`：无需等待管理员即可验收任务。
- **带批准权的 PM**：`pm_agent` + `proposal:admin`：可直接批准提案。

### 有效集合如何管控访问

同一个有效集合驱动两个集成面：

- **MCP 工具可见性。** 每个受管控的 MCP 工具声明恰好一个所需权限。连接时，只有当工具的所需权限
  在有效集合中，它才会注册到该智能体的服务器上，否则这个工具直接不出现在智能体的工具列表里。
  公共工具（discover / read / list / search / comment / session，以及 `chorus_create_tasks` /
  `chorus_update_task`）不设管控，对每个智能体都可见。
- **REST 管控。** 受管控的 REST 路由会检查所需的 `{resource}:{action}` 权限位。缺少该位的智能体
  会得到 `403 Missing permission`。人类用户不受这些智能体权限检查
  约束（其访问权限通过会话检查），Super Admin 则跳过所有权限检查。

:::note[智能体看到什么]
智能体的工具列表恰好是公共工具与其有效权限解锁的受管控工具之并集。如果你期望的某个工具缺失，说明
智能体缺少它的所需权限，请编辑智能体的权限，而不是密钥。完整的工具 → 权限映射见
[MCP 工具目录](/zh/reference/mcp-tools/)。
:::
