feat(auth): 服务端托管 Google OAuth;修复未解锁 vault 时 bootstrap
- API:桌面登录 session、Google 托管回调与轮询 - Desktop:轮询登录;bootstrap 在 vault 未解锁时不返回 shell,避免跳过主密码 - 文档与 deploy/.env.example 对齐 GOOGLE_OAUTH_* 与 SECRETS_PUBLIC_BASE_URL
This commit is contained in:
96
README.md
96
README.md
@@ -27,8 +27,42 @@ cargo run -p secrets-desktop
|
||||
- `apps/desktop/src-tauri/tauri.conf.json` 中 `build.frontendDist` 指向 `apps/desktop/dist`
|
||||
- 当前仓库会直接提交 `apps/desktop/dist/` 下的桌面端静态资源
|
||||
- 因此新机器 clone 后,无需额外前端构建步骤即可启动 desktop
|
||||
- Google Desktop OAuth 的 `client_secret_*.json` **不会入库**
|
||||
- 新机器需要自行提供该文件,并通过 `GOOGLE_OAUTH_CLIENT_FILE` 指向它;推荐使用绝对路径
|
||||
- 官网 DMG 正式分发不依赖本地 `client_secret_*.json`
|
||||
- Google OAuth 凭据只配置在 API 服务端,desktop 通过浏览器完成托管登录
|
||||
|
||||
## 官网 DMG 的服务端 OAuth 配置
|
||||
|
||||
官网 DMG 正式分发时,**Google OAuth 只配置在 API 服务端**。桌面端不需要本地 `client_secret_*.json`,也不直接向 Google 换 token。
|
||||
|
||||
建议先复制 `deploy/.env.example` 为 `.env`,然后至少配置以下变量:
|
||||
|
||||
```bash
|
||||
SECRETS_PUBLIC_BASE_URL=https://secrets.example.com
|
||||
GOOGLE_OAUTH_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com
|
||||
GOOGLE_OAUTH_CLIENT_SECRET=your-google-oauth-client-secret
|
||||
GOOGLE_OAUTH_REDIRECT_URI=https://secrets.example.com/auth/google/callback
|
||||
```
|
||||
|
||||
变量含义:
|
||||
|
||||
- `SECRETS_PUBLIC_BASE_URL`:桌面端打开浏览器时访问的 API 外网基地址,必须是用户浏览器能访问到的公开地址
|
||||
- `GOOGLE_OAUTH_CLIENT_ID`:Google Cloud Console 中为服务端登录流程配置的 OAuth Client ID
|
||||
- `GOOGLE_OAUTH_CLIENT_SECRET`:对应的 Client Secret,只能保留在服务端
|
||||
- `GOOGLE_OAUTH_REDIRECT_URI`:Google 登录完成后回调到 API 的地址,必须与 Google Console 中登记的回调地址完全一致
|
||||
|
||||
配置步骤建议:
|
||||
|
||||
1. 在 Google Cloud Console 创建或选择 OAuth Client
|
||||
2. 把授权回调地址加入允许列表,例如 `https://secrets.example.com/auth/google/callback`
|
||||
3. 把上面的 4 个变量配置到 API 服务的运行环境中
|
||||
4. 确认 `SECRETS_PUBLIC_BASE_URL` 与 `GOOGLE_OAUTH_REDIRECT_URI` 使用同一公开域名
|
||||
5. 重启 API 服务后,再用 desktop / DMG 验证浏览器登录流程
|
||||
|
||||
注意:
|
||||
|
||||
- `GOOGLE_OAUTH_CLIENT_SECRET` 不要提交到仓库
|
||||
- `GOOGLE_OAUTH_REDIRECT_URI` 不要写成 `localhost`,正式分发应使用官网可访问域名
|
||||
- 如果 API 部署在反向代理后面,`SECRETS_PUBLIC_BASE_URL` 应填写用户实际访问的 HTTPS 地址,而不是内网监听地址
|
||||
|
||||
## 当前能力
|
||||
|
||||
@@ -69,25 +103,27 @@ cargo test --locked
|
||||
- 桌面端登录态仅在当前进程内有效,不持久化 `device token`
|
||||
- 本地 daemon 默认监听 `http://127.0.0.1:9515/mcp`
|
||||
- daemon 通过活跃 desktop 进程提供的本地会话转发访问 API;desktop 进程退出后所有工具不可用
|
||||
- `target_exec` 会显式读取真实 secret 值后再生成 `TARGET_*` 环境变量
|
||||
- `target_exec` 会显式读取真实 secret 值后再生成 `TARGET_`* 环境变量
|
||||
- 不保留 `secrets_env_map`
|
||||
|
||||
### Canonical MCP 工具
|
||||
|
||||
| 工具 | 说明 |
|
||||
| --- | --- |
|
||||
| `secrets_entry_find` | 从 desktop 已解锁本地 vault 搜索对象,支持 `query` / `folder` / `type` |
|
||||
| `secrets_entry_get` | 读取单条本地对象,并返回当前 secrets 的真实值 |
|
||||
| `secrets_entry_add` | 在本地 vault 创建对象,可选附带初始 secrets |
|
||||
| `secrets_entry_update` | 更新本地对象的 folder / type / name / metadata |
|
||||
| `secrets_entry_delete` | 将本地对象标记为删除 |
|
||||
| `secrets_entry_restore` | 恢复本地已删除对象 |
|
||||
| `secrets_secret_add` | 向已有本地对象新增 secret |
|
||||
| `secrets_secret_update` | 更新本地 secret 名称、类型或内容 |
|
||||
| `secrets_secret_delete` | 删除单个本地 secret |
|
||||
| `secrets_secret_history` | 查看单个本地 secret 的历史版本 |
|
||||
| `secrets_secret_rollback` | 将单个本地 secret 回滚到指定版本 |
|
||||
| `target_exec` | 用本地对象的 metadata 和 secrets 生成 `TARGET_*` 环境变量并执行本地命令 |
|
||||
|
||||
| 工具 | 说明 |
|
||||
| ------------------------- | --------------------------------------------------------- |
|
||||
| `secrets_entry_find` | 从 desktop 已解锁本地 vault 搜索对象,支持 `query` / `folder` / `type` |
|
||||
| `secrets_entry_get` | 读取单条本地对象,并返回当前 secrets 的真实值 |
|
||||
| `secrets_entry_add` | 在本地 vault 创建对象,可选附带初始 secrets |
|
||||
| `secrets_entry_update` | 更新本地对象的 folder / type / name / metadata |
|
||||
| `secrets_entry_delete` | 将本地对象标记为删除 |
|
||||
| `secrets_entry_restore` | 恢复本地已删除对象 |
|
||||
| `secrets_secret_add` | 向已有本地对象新增 secret |
|
||||
| `secrets_secret_update` | 更新本地 secret 名称、类型或内容 |
|
||||
| `secrets_secret_delete` | 删除单个本地 secret |
|
||||
| `secrets_secret_history` | 查看单个本地 secret 的历史版本 |
|
||||
| `secrets_secret_rollback` | 将单个本地 secret 回滚到指定版本 |
|
||||
| `target_exec` | 用本地对象的 metadata 和 secrets 生成 `TARGET_`* 环境变量并执行本地命令 |
|
||||
|
||||
|
||||
## AI 客户端配置
|
||||
|
||||
@@ -115,7 +151,7 @@ cargo test --locked
|
||||
- 服务端保存 `vault_objects` 与 `vault_object_revisions`
|
||||
- desktop 本地保存 `vault_objects`、`vault_object_history`、`pending_changes`、`sync_state`
|
||||
- 搜索、详情、reveal、history 主要在本地已解锁 vault 上完成
|
||||
- 服务端负责 `auth/device` 与 `/sync/*`,不再承担明文搜索与明文 reveal
|
||||
- 服务端负责 `auth/device` 与 `/sync/`*,不再承担明文搜索与明文 reveal
|
||||
|
||||
主要表:
|
||||
|
||||
@@ -129,23 +165,25 @@ cargo test --locked
|
||||
|
||||
字段职责:
|
||||
|
||||
| 位置 | 字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `vault_objects` | `object_id` | 同步对象标识 |
|
||||
| `vault_objects` | `object_kind` | 当前对象类别,当前主要为 `cipher` |
|
||||
| `vault_objects` | `revision` | 服务端对象版本 |
|
||||
| `vault_objects` | `ciphertext` | 密文对象载荷 |
|
||||
| `vault_objects` | `content_hash` | 密文摘要 |
|
||||
| `vault_objects` | `deleted_at` | 对象级删除标记 |
|
||||
| `vault_object_revisions` | `revision` / `ciphertext` | 服务端对象历史版本 |
|
||||
|
||||
| 位置 | 字段 | 说明 |
|
||||
| ------------------------ | ------------------------- | --------------------- |
|
||||
| `vault_objects` | `object_id` | 同步对象标识 |
|
||||
| `vault_objects` | `object_kind` | 当前对象类别,当前主要为 `cipher` |
|
||||
| `vault_objects` | `revision` | 服务端对象版本 |
|
||||
| `vault_objects` | `ciphertext` | 密文对象载荷 |
|
||||
| `vault_objects` | `content_hash` | 密文摘要 |
|
||||
| `vault_objects` | `deleted_at` | 对象级删除标记 |
|
||||
| `vault_object_revisions` | `revision` / `ciphertext` | 服务端对象历史版本 |
|
||||
|
||||
|
||||
## 认证与事件
|
||||
|
||||
当前登录流为 Google Desktop OAuth:
|
||||
|
||||
- 桌面端使用系统浏览器拉起 Google 授权
|
||||
- 使用本地 loopback callback + PKCE
|
||||
- API 校验 Google userinfo 后发放 `device token`
|
||||
- API 服务端负责发起 OAuth、处理 callback、校验 Google userinfo
|
||||
- desktop 通过创建一次性 login session 并轮询状态获取 `device token`
|
||||
- 登录与设备活动写入 `auth_events`
|
||||
|
||||
## 项目结构
|
||||
|
||||
Reference in New Issue
Block a user