# Dominds 认证（设计）

英文版：[English](./auth.md)

本文档指定了 Dominds WebUI + API 访问的**认证行为**。

## 目标

- **生产环境安全**：防止 Dominds 实例意外暴露（尤其是绑定到非 localhost 时）。
- **低运营开销**：单一共享密钥，设置一次，到处使用。
- **良好体验**：WebUI 可以在需要时提示输入密钥，并记住它以便将来访问。
- **便捷的"自动认证"**：允许一键/复制粘贴 URL 来为当前会话预填认证。

## 非目标

- 多用户账户、角色/权限、OAuth、SSO
- 密钥轮换工作流、审计日志或细粒度访问控制
- 开发模式的认证（明确禁用）

## 术语

- **认证密钥**：用于验证请求的共享密钥。
- **开发模式**：认证被禁用的运行时环境。
- **生产模式**：生产运行时，其中认证行为由环境启用/控制。
- **自动认证 URL**：包含认证密钥作为查询参数的 WebUI 页面 URL，用于自动认证。

## 模式规则

### 开发模式

- **认证始终禁用。**
- `DOMINDS_AUTH_KEY`（如果存在）在开发模式下**无效**。

### 生产模式

认证行为由 `DOMINDS_AUTH_KEY` 环境变量控制：

| `DOMINDS_AUTH_KEY` 值 | 有效行为                                   |
| --------------------- | ------------------------------------------ |
| **未设置**            | **启用认证**，使用**随机生成**的认证密钥   |
| **空字符串**          | **禁用认证**                               |
| **非空字符串**        | **启用认证**，使用提供的字符串（逐字使用） |

注意：

- 认证密钥被视为**不透明字符串**（不进行修剪、规范化或大小写折叠）。
- 生成的认证密钥必须是**加密安全的**并且**URL 安全可嵌入**（经过 URL 编码后）。

## 认证机制（生产模式，已启用时）

- 每个 API 请求必须使用 HTTP `Authorization` 头进行认证：
  - `Authorization: Bearer <auth-key>`

- "API 请求"包括：
  - 改变或显示 rtws（运行时工作区）/对话状态的 HTTP 端点
  - WebUI 用于实时更新的 WebSocket 连接

实现说明（WebUI）：浏览器在 WebSocket 握手期间无法附加自定义 `Authorization` 头。因此，Dominds WebUI 通过 `Sec-WebSocket-Protocol` 传输认证密钥，作为形式为 `dominds-auth.<auth-key>`（纯文本）的子协议，服务器接受任一机制。

为了使编码不生效，认证密钥必须是 HTTP token 安全的字符串（RFC 7230 `tchar` 集）。

如果认证**禁用**，服务器必须接受没有认证头的请求和 WebSocket 连接。
如果认证头在认证禁用时存在，服务器必须忽略它。

## 服务器端认证结果（生产模式，已启用时）

服务器强制执行以下结果：

- 如果认证头**缺失**，请求/连接必须被拒绝为未授权。
- 如果认证头**存在但不正确**，请求/连接必须被拒绝为未授权。
- 如果认证头**正确**，请求/连接正常进行。

服务器应使用一致的"未授权"响应，以便客户端可以可靠地检测认证失败。

## WebUI 行为

### 认证密钥的来源

WebUI 一次只使用一个有效的认证密钥，按此优先级顺序选择：

1. **URL 查询参数** `auth`（自动认证模式）
2. **浏览器 localStorage**（记住的密钥）
3. **用户提示输入**（交互式输入）

### localStorage 规则

- 如果 WebUI 使用的密钥来自 **localStorage**，它必须将该密钥作为 Bearer 令牌附加到所有 API 请求。
- 如果用户**手动输入**密钥（不是来自 URL），WebUI 必须：
  - 立即将其用于 API 请求
  - 将其持久化到 **localStorage** 以供以后使用
- 如果 **localStorage** 中的密钥被服务器拒绝，WebUI 必须：
  - 提示用户输入新密钥
  - 在认证成功确认后替换 localStorage 中存储的密钥

### 自动认证 URL 规则（`?auth=...`）

当 WebUI 页面 URL 中存在 `auth` 查询参数时：

- WebUI 必须使用 `auth` 参数值作为 API 请求的认证密钥。
- WebUI 必须**不**读取 localStorage。
- WebUI 必须**不**写入 localStorage。

如果在 URL 中存在 `auth` 时认证失败：

- WebUI 必须从 `window.location` 中移除 `auth` 参数（使其不再出现在地址栏中）。
- 移除后，WebUI 必须转换到正常的交互流程：
  - 提示用户输入认证密钥（然后照常在成功后将其持久化到 localStorage）。

## CLI 要求（WebUI 子命令）

### "自动认证 URL"控制台输出

在**生产模式**下启动 WebUI 服务器时：

- 如果认证**启用**（无论是生成的还是显式设置的），WebUI 子命令必须向控制台记录一个**"自动认证 URL"**字符串，其中包含作为查询参数的认证密钥。

示例（说明性）：

```txt
auto auth url: http://<host>:<port>/?auth=<url编码的认证密钥>
```

- 如果认证**禁用**，子命令应记录认证已禁用，且**不得**打印认证密钥。

### `--nobrowser`

默认情况下，WebUI 子命令会自动打开浏览器。要选择退出，使用 `--nobrowser`：

- 如果认证**启用**，它必须在默认浏览器中打开**自动认证 URL**。
- 如果认证**禁用**，它必须打开正常的 WebUI URL（无 `auth` 参数）。

`--nobrowser` 不会改变认证行为；它只会改变人体工程学。

## 安全与隐私说明

- 认证密钥是**共享密钥**；拥有该密钥的人拥有 API 表面所允许的完全访问权限。
- 自动认证 URL 在查询字符串中包含认证密钥，可能通过以下方式泄露：
  - 复制/粘贴和截图
  - 浏览器历史记录
  - Referrer 头（取决于导航）
  - 捕获 URL 的日志或监控

操作员应将自动认证 URL 视为敏感信息，避免广泛分享。
