> 原始 Markdown 孪生体（构建期从源 Markdown 生成）。渲染页：https://docs.gatellm.io/zh-CN/howto/upstream-sso-login · 文档索引：https://docs.gatellm.io/zh-CN/llms.txt


# 上游 SSO 凭证登录

有些上游（如 Kiro）不接受静态 API key，而要你用 OAuth 登录拿一份**会过期的刷新凭证**。网关转发请求时，得拿这份凭证去换一个临时 access token、塞进发往上游的请求头。本页讲管理员怎么**一次性把这份凭证登录下来、导进网关**。

无需手工建上游：**登录本身就是全部操作**——首次登录会自动创建厂商对应的上游，此后的登录把新账号的凭证追加到同一个上游下。已登录后，可在控制台 → **上游** → 该 SSO 上游（带 SSO 徽标）→ 凭证区块里查看各账号凭证的状态，或复制那条 `docker run … login …` 命令继续加账号。

> 核心机制：**登录发生在管理员本机，不在网关**。网关是远端服务，接不住浏览器回环重定向；所以网关镜像自带一个 `login` 子命令，你在自己机器上跑它——它在本机拉起一个临时监听接住浏览器回调、完成 OAuth 授权、拿到刷新凭证后，用控制台访问密钥调网关的导入 API 把凭证上报，随即退出、不落地任何凭据。网关全程只接收一个已铸好的凭证，暴露面最小。

> **别和 [配置 SSO 企业登录](/zh-CN/howto/configure-sso.md) 搞混**：那一页讲『用企业 IdP 认证**调用网关的人**』（数据面 token / 控制台企业登录）；本页讲『管理员登录**上游厂商**（如 Kiro）拿一份网关转发用的刷新凭证』。方向相反：一个认证进网关的流量，一个给网关去上游用。

## 第一步：先配好控制台访问密钥

这是**前置条件**，没配整条流程会卡在 401。`login` 子命令向网关认证用的是**控制台访问密钥**（`web_console.secret_key`），不是数据面 client access key。

网关配置 `[web_console]` 段的 `secret_key` 是一把长期有效的静态密钥（bcrypt 哈希存储），命中即注入**完全管理员权限**。配置要求：

- 非空且 ≥ 12 字符，否则配置校验直接报错；
- 默认未配置（`None`），**不会自动生成**，必须手动设置；
- 数据库存储模式下，在 `server.toml` 的 `[web_console]` 段写 `secret_key = "你的密钥"`；Docker 镜像通过环境变量 `CONSOLE_SECRET_KEY` 注入。

> ⚠️ 这把密钥 = 完全管理员权限。传递用环境变量或 `:ro`（只读）文件挂载、用完即删；不要写进命令行参数、版本控制或聊天记录。

## 第二步：确认容器能连到网关控制台

`login` 容器要把导入请求发到网关控制台。控制台的远端访问由 `allow_remote` 控制，注意**两层默认值不同**：**配置文件默认 `false`（只放行本机回环）**，而**官方 Docker 镜像经 `CONSOLE_ALLOW_REMOTE=true` 默认放行远程**（见 [环境变量配置参考](/zh-CN/reference/configuration.md) 与 [控制台登录与角色 → 安全](/zh-CN/console/login-and-roles.md#security)）。若你的网关按默认 `false` 生效（如 bare metal / 源码运行），容器经网桥网络连过来的源 IP 不是回环，会被挡成 403。**两条通路二选一**：

1. **`docker run --network host`**（推荐，默认配置不动）：容器共享宿主网络，源 IP 即回环，默认配置即可放行。
2. **`allow_remote = true`**（改网关配置）：放开控制台的远端访问。注意这会扩大控制台暴露面，且同机反向代理下来源 IP 是 `127.0.0.1`、既有检查挡不住经代理进来的远端客户端——既有语义，非本次引入。

> 检查依据是 TCP 对端地址，不看 `X-Forwarded-For`，所以 `--network host` 是最省心的。

## 第三步：运行登录命令

在能打开浏览器的机器上跑下面的命令。访问密钥经**环境变量**传入；`login` 刻意不提供 `--access-key <明文>` 这类命令行参数形式——命令行参数会进 `ps` 进程列表、shell history 与 `docker inspect`，密料不落命令行。`--entrypoint protoflux` 必须显式写明，否则 lima/colima 这类 Docker wrapper 会把裸 `login` 当宿主命令解析，去执行系统的 `/usr/bin/login`（报错 `Cannot possibly work without effective root`）：

```bash
docker run --rm -it --network host \
  --entrypoint protoflux \
  -e PROTOFLUX_ACCESS_KEY='<secret_key 的值>' \
  <网关镜像> login \
    --vendor kiro \
    --gateway http://localhost:7890
```

> 把 `<网关镜像>` 换成你**部署网关用的那个镜像引用**——就是 [Docker 单机跑通](/zh-CN/quickstart/docker-single-node.md) 第 1 步 `docker pull` / `docker run` 用的同一个镜像（官方发布版形如 `ghcr.io/<组织>/<镜像>:<tag>`）。`login` 子命令内置在这个镜像里，无需另装。

参数说明：

| 参数 | 含义 |
|---|---|
| `-e PROTOFLUX_ACCESS_KEY=<值>` | 第一步配好的 `secret_key`，经环境变量传入（推荐方式） |
| `--vendor kiro` | 要登录的上游 SSO 厂商 id，与上游表单里选的 `sso_vendor` 一致 |
| `--gateway http://localhost:7890` | 网关控制台地址。`--network host` 下用 `localhost`；否则换成宿主 IP 或 `host.docker.internal`。**必须是 https，或 http + 回环/私网地址**——公网明文 http 一律拒绝 |
| `--account <账号>`（可选） | 该凭证的账号标识（如登录邮箱）。同名重登**替换**旧凭证，不同名**追加**新账号。缺省自动取厂商返回的账号身份；厂商不给时落"未命名账号"槽位 |
| `--upstream-id 7`（可选） | 目标上游的稳定 id（控制台上游列表里能看到的数字 id）。**一般不用填**：缺省时网关自动解析或创建该厂商的上游 |
| `--port 3128`（可选） | 本机回调监听端口。缺省自动选厂商 IdP 白名单里的第一个空闲端口（kiro 白名单首项 3128——IdP 只认官方 IDE 注册的固定端口集，临时端口会被拒）。用 `docker -p 127.0.0.1:3128:3128` 发布端口时须钉死同一白名单端口并保持一致；`--network host` 下可省略 |
| `--access-key-file <容器内路径>`（可选） | 环境变量的替代档：把密钥存成本地文件，`-v /path/to/key.txt:/key:ro` 只读挂载后用本参数指向它（适配 docker secret 等文件型密钥管理） |

密钥取值三档（按此顺序）：`--access-key-file` 文件 → 环境变量 `PROTOFLUX_ACCESS_KEY` → 都没有则无回显交互输入。三档都不经命令行参数。

命令跑起来后会发生这些事：它在本机回环白名单端口（kiro 默认首个空闲的 3128）拉起临时监听 → 打开浏览器跳去 IdP 登录 → 你在浏览器里完成授权 → IdP 带授权码回跳本机监听 → 子命令用 PKCE（Proof Key for Code Exchange）换到刷新凭证 → 调网关导入 API 上报 → 退出。全程**刷新凭证不落你本机、不进网关日志**。

> 容器内通常唤不起浏览器，命令会把授权 URL 打印出来，复制到宿主浏览器打开即可。

## 企业登录：IAM Identity Center（AWS IDC）

上面的 PKCE 流程覆盖 Kiro 门户里的 **Builder ID、Google、GitHub** 等社交登录。如果你的企业用 **AWS IAM Identity Center**（原 AWS SSO）管理 Kiro 访问，登录走的是**同一个 `login` 命令、同一个门户入口**，区别只在浏览器里选哪种登录方式——命令本身不用加任何参数，CLI 会根据门户回调的形状自动分流。

操作步骤：

1. 照常跑 `login --vendor kiro …`，浏览器打开 Kiro 门户登录页；
2. 在登录页选 **IAM Identity Center**（而非 Builder ID / Google / GitHub）；
3. 按提示输入管理员给你的**企业 start URL**（形如 `https://d-example12345.awsapps.com/start` 的 AWS IDC 租户地址，具体值由企业管理员提供）；
4. 门户回跳本机监听——这次回调**不带授权码**，而是带 `login_option=awsidc` + 企业 start URL + IDC 区域。CLI 识别到这个中间回调后，会回浏览器一个"正在完成设备授权"页面，随后在终端打印一条**设备验证链接**；
5. 打开那条验证链接（或终端打印的 URL），在浏览器里完成设备授权；
6. 授权完成后，CLI 拿到 access token + refresh token，照常上报网关。

技术上的区别（管理员通常无需关心，仅供排查）：社交登录的凭证刷新打 Kiro 桌面端点；IDC 凭证的刷新打 AWS SSO-OIDC 的 `oidc.{区域}.amazonaws.com/token`，且每个企业的 IDC 区域不同——CLI 会把企业 start URL 和 IDC 区域随凭证一起存进网关，续期时按凭证读取，不用你配置。导入的 IDC 凭证行 `来源` 列显示 `kiro_idc`，社交登录显示 `kiro_desktop`，两种凭证可在同一个 Kiro 上游下并存、网关自动轮换。

> **IDC 凭证 90 天需重登**：服务器后台任务会自动续期 access token（每 60 秒扫描、提前 5 分钟刷新），续的是 access token，不是客户端密钥。设备流程注册的 OIDC 客户端密钥（`client_id`/`client_secret`）有效期约 90 天，到期后刷新会开始失败——此时刷新无法自动恢复（重试仍用同一对过期密钥），凭证会转成终态并标注"客户端密钥过期，需重新登录"。届时重新跑一次上面的登录命令（同一个账号会替换旧凭证）即可，无需删旧行。社交登录凭证不涉及客户端密钥，无此限制。

## 多账号：多登录一次就多一个账号

一个厂商（如 kiro）在网关里**始终只有一个上游**，下面挂多个账号的凭证——和"一个上游配多把 API key"是同一逻辑，网关自动在几个账号之间轮换分摊请求。**再加一个账号 = 把上面的登录命令再跑一遍**（用另一个账号完成授权）：

- 新账号的凭证追加到同一个上游下，即刻参与轮换，无需重启或改配置；
- 想让各行能明确区分，加 `--account 邮箱或别名`；缺省自动取厂商返回的账号身份；
- 同一个账号重新登录（`--account` 相同，或厂商返回的身份相同）会**替换**该账号的旧凭证——凭证失效时重新登录一次即可修复。

## 账号凭证的管理（查看 / 删除）

控制台 → **上游** → 点开该 SSO 上游 → 凭证区块列出**每个账号一行**：账号标识、状态（active / expired / error）、来源、过期时间。

- **删除某个账号**：点该行右侧的删除图标，再点"删除"确认。删除后该账号立刻不再承接流量、网关也不再替它续期；想恢复时重新登录导入即可。
- 删除只影响这一个账号行，同上游的其他账号不受影响。

## 凭证由网关自动续期

凭证导入后不需要任何维护：网关后台任务**在到期前主动续期**（默认每 60 秒扫描一次、提前 5 分钟刷新），即使该上游长期没有流量也不会让凭证静默过期。续期偶尔失败时网关自动退避重试；状态为 `error` 的行会自愈，不需要人工干预——唯一需要人处理的是凭证被上游吊销（状态 revoked，通常是长时间未续期或在上游侧主动登出），此时重新跑一次登录命令导入即可。

多实例部署同样无需配置：各实例自动互斥（每个凭证同一时刻只有一个实例在续期），续期结果自动传播到其余实例。相关参数（`sso_credentials.*`）见 [环境变量配置参考 → 仅配置文件可配的字段](/zh-CN/reference/configuration.md#config-file-only-fields)，默认值即可用。

## 兜底：手动托举

登录失败（IdP 端点行为变化、网络问题等）时，可以走**手动托举**：在 Kiro 官方界面/客户端完成登录，把产生的刷新凭证复制下来，在控制台上游凭证区块的"手动粘贴"框里贴进去点导入（可顺带给一个账号标识，用于多账号去重）。零脚本、零登录端点依赖，是任何厂商的通用兜底。

## 排查

- **401**：`secret_key` 没配（`web_console.secret_key` 为空，控制台 API 未启用）或值不匹配。两者后端无法区分，错误文案同时列出两种可能。
- **403**：控制台远端访问被拒。用 `--network host`，或在网关配置设 `allow_remote = true`。
- **密钥刚改了还认旧值**：`verify_bearer` 有 300 秒正向缓存（以 token 哈希为 key），轮换后旧密钥最长 300 秒内仍可能有效；超时后自动失效。
- **连续试错被 ban**：控制台有失败计数与 IP 封禁。排错换密钥试错多次会触发，等封禁时长过后再试或换 IP。
- **回调收不到**：确认 `--port` 与 `docker -p` 映射一致、端口没被占用；`--network host` 下无需 `-p`。
- **浏览器报 redirect_mismatch**：回调端口不在厂商 IdP 白名单里。缺省行为已自动选白名单端口；若手动指定了 `--port`，改成白名单内的（kiro：3128、4649、6588、8008、9091 等）。
- **`Cannot possibly work without effective root`**：命令没写 `--entrypoint protoflux`，lima/colima 等 Docker wrapper 把裸 `login` 解析成了系统命令 `/usr/bin/login`（非特权拒绝）。补上 `--entrypoint protoflux` 即可。

## 运维须知

- 经 `secret_key` 上报的导入操作，审计日志里 actor 统一记为 `secret_key`，无法区分是哪个工具/哪次登录发起的。
- 这把密钥命中即完全管理员权限，属长期高权限凭证：`:ro` 挂载、用完即删；轮换走改配置 + 重载（注意 300 秒缓存窗口）。
- 导入的刷新凭证用主密钥 `ENCRYPTION_KEY` 加密存数据库；网关未配它时**拒绝工作**（fail-closed，而非明文降级）。没配的话先 `openssl rand -hex 32` 生成一把并带上它重启，见 [环境变量配置参考 → ENCRYPTION_KEY](/zh-CN/reference/configuration.md#storage-multi-instance)——**务必备份，丢了已加密的凭证永久不可读**。

**下一步**：[配置 SSO 企业登录](/zh-CN/howto/configure-sso.md) 看反方向——用企业 IdP 认证调用网关的人；[上游与模型字段](/zh-CN/reference/upstreams-models-fields.md) 看上游完整字段；[端点 · 认证 · 协议互通](/zh-CN/reference/endpoints.md) 看认证顺序全景。
