> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 通过您的 IdP 为气隙版 Devin 配置 SSO（单点登录）

> 要求用户通过您自己的 OIDC 身份提供商登录气隙版 Devin Desktop 和 CLI：认证文件、客户端设置、基于组的访问控制、审计事件和 IDP 错误代码。

> [!NOTE]
>
> 本文档适用于 Devin 的联邦部署。[返回 Devin 文档](/zh/get-started/devin-intro)

默认情况下，气隙构建的访问权限仅由[许可证文件](/zh/federal/air-gapped/license)控制。你还可以进一步要求每个用户通过**您自己的 OIDC 身份提供商**登录。SSO 是叠加在许可证之上的一层控制：许可证仍然必不可少，登录流程则通过你所配置的签发方进行。

## 创建认证文件

| | |
| - | - |
| 默认路径 | `/etc/devin/auth.json` |
| 默认路径 (Windows) | `C:\ProgramData\Devin\auth.json` |
| 覆盖设置 | `DEVIN_AUTH_FILE=<path>` |

该文件只能指定一种模式。如果该文件不存在，产品将按文件中包含 `"mode": "license"` 的情况运行。

```json /etc/devin/auth.json theme={null}
{
  "schema_version": 0,
  "mode": "sso",
  "issuer": "https://sso.example.com",
  "client_id": "devin-cli",
  "scopes": ["openid", "profile", "email"],
  "groups_claim": "groups",
  "required_group": "devin-users",
  "additional_endpoint_hosts": []
}
```

| 字段 | 必填 | 含义 |
| - | - | - |
| `schema_version` | 是 | 必须为 `0`。 |
| `mode` | 是 | `"sso"` 表示要求登录；`"license"` 表示仅凭许可证访问 (文件不存在时的默认值) 。 |
| `issuer` | 是 (sso) | OIDC 签发方 URL。必须使用 `https://` (仅回环主机允许使用普通 `http://`) ，且不得包含查询字符串、片段或内嵌凭据。系统会通过 `<issuer>/.well-known/openid-configuration` 执行自动发现。 |
| `client_id` | 是 (sso) | 您在 IdP 中注册的客户端。 |
| `scopes` | 否 | 默认为 `openid profile email`。如需设置，必须包含 `openid`。若 IdP 声明支持 `offline_access`，系统会自动请求该作用域，以获取 refresh token。 |
| `groups_claim` | 否 | 携带组成员关系的 ID 令牌认领。默认为 `groups`。 |
| `required_group` | 否 | 设置后，仅当该值出现在 groups 认领中时才允许登录，否则拒绝登录。 |
| `additional_endpoint_hosts` | 否 | 允许 IdP 自动发现文档在其上声明端点的额外主机。如无必要，请留空；参见[自动发现的端点必须位于签发方上](#discovered-endpoints-must-stay-on-the-issuer)。 |

未知字段会被拒绝。在 `license` 模式下，仅允许使用 `schema_version` 和 `mode`。

> [!TIP]
>
> 如果您的 IdP 使用由私有 CA 签发的证书，请将 `DEVIN_CA_BUNDLE` 设置为 PEM 文件包，以便 CLI 信任该证书；参见[网络与证书](/zh/federal/air-gapped/network-and-certificates)。

## 在您的 IdP 中注册客户端

CLI 以**公开客户端**身份运行 OIDC **带 PKCE (S256) 的授权码流程**，不使用客户端密钥。您注册的客户端必须满足以下要求：

1. 是一个**公开**客户端 (无客户端密钥) ，其 ID 与您在 `client_id` 中填写的值一致 (例如 `devin-cli`) ，已启用授权码授权类型，且 PKCE 方法为 `S256`。
2. 已将 `http://127.0.0.1/callback` 注册为有效的重定向 URI。在浏览器流程中，CLI 会在随机的回环端口 (`http://127.0.0.1:<port>/callback`) 上监听；在手动流程中，则使用固定的 `http://127.0.0.1/callback`。根据 RFC 8252，IdP 应忽略回环端口；如果您的 IdP 不忽略端口，请注册一个匹配的通配符地址。
3. 如果要使用 `required_group`，ID 令牌必须包含 `groups_claim` 中指定的组成员资格认领 (默认为 `groups`) 。
4. IdP 在刷新时会返回 ID 令牌 (授予 `openid` 作用域后，大多数 IdP 都会返回) ，且该客户端的 ID 令牌有效期较短。原因请参阅[撤销如何传播](#how-revocation-propagates)。

## 登录

在工作站的终端中登录：

```sh theme={null}
devin auth login
```

```text theme={null}
Opening your browser to sign in with your identity provider...
If the browser does not open, visit:

  https://sso.example.com/authorize?client_id=devin-cli&...

(Use `devin auth login --force-manual-token-flow` on headless machines.)
Logged in as jsmith via https://sso.example.com.
Token expires at 2026-03-02 15:04:05 UTC.
```

在无头主机上或通过 SSH 连接时，请使用手动流程。CLI 会输出一个 URL，你可以在任意机器的浏览器中打开它；登录后，浏览器会被重定向到一个无法加载的 `127.0.0.1` URL。复制这个完整的 URL (包括其中的查询参数) ，再粘贴回终端即可。

```sh theme={null}
devin auth login --force-manual-token-flow
```

```text theme={null}
Open this URL in a browser (on any machine) and sign in:

  https://sso.example.com/authorize?client_id=devin-cli&...

After signing in, the browser is redirected to a 127.0.0.1 URL that will not load — copy that full URL (including its query parameters) and paste it here.
Paste the full redirect URL:
```

Devin Desktop 通过内置的智能体沿用同一套登录信息；用户若尚未登录，系统会在开始工作前提示其在 Devin 设置中登录。

## 检查状态并退出登录

```sh theme={null}
devin auth status
```

```text theme={null}
Auth mode: SSO (issuer: https://sso.example.com)
Status: logged in
User: jsmith
Subject: 6f1c2a0e-...
Groups: devin-users
Token expires at 2026-03-02 15:04:05 UTC.
```

如果缓存的登录信息不存在或已无法刷新，则会输出 `Status: not logged in (<reason>)`，后跟 `Run \`devin auth login\` to sign in.\`

```sh theme={null}
devin auth logout
```

```text theme={null}
Logged out: local SSO tokens deleted.
```

退出登录仅在本地生效：只会删除令牌缓存，不会与 IdP 通信。

## CLI 会校验哪些内容

ID 令牌在本地进行校验：基于 IdP 的 JWKS 验证签名，并校验 `iss`、`aud`、过期时间 (允许 120 秒的时钟偏差) 、登录时的 `nonce` 以及组访问控制。ID 令牌和 refresh token 缓存在本地凭据存储中，文件权限为仅所有者可访问 (`0600`) ，并会在后台向 IdP 静默刷新。

如果已配置 SSO (单点登录) ，但没有有效的登录状态 (且静默刷新失败) ，CLI 会在启动时报错退出，提示用户运行 `devin auth login`。

CLI 不会跟随 IdP 返回的任何 HTTP 重定向：如果自动发现端点、JWKS 端点或令牌端点返回 `3xx`，登录即会失败，错误信息中会指明出错的端点。因此，`issuer` 必须指向 IdP 实际提供服务的 URL。

### 自动发现的端点必须与签发方位于同一来源

通过自动发现获取的 `authorization_endpoint`、`token_endpoint` 和 `jwks_uri` 必须与 `issuer` 具有**相同的协议、主机和端口** (默认端口视为相同) 。如果端点位于其他主机上，将被拒绝，登录也会失败，并显示类似以下的错误：

```text theme={null}
IdP discovery document advertises jwks_uri 'https://keycloak.corp.internal:8443/realms/devin/protocol/openid-connect/certs'
on host 'keycloak.corp.internal:8443', which is not the configured issuer's host 'sso.corp.example:443';
refusing to trust it. If this IdP really serves that endpoint from a different host,
add "keycloak.corp.internal" to `sso.additional_endpoint_hosts` in the auth file
```

某些 IdP 拓扑确实会从多个主机提供端点，这属于正常情况。常见的本地部署场景是启用了 `hostname-backchannel-dynamic=true` 的 Keycloak，此时 `token_endpoint` 和 `jwks_uri` 会被重写为 CLI 连接时所用的内部主机名。只有在你已对照 IdP 自身的配置确认该额外主机确属有意设置时，才可以添加它：

```json theme={null}
"additional_endpoint_hosts": ["keycloak.corp.internal"]
```

条目格式为 `host` (任意端口) 或 `host:port`，匹配时不区分大小写。不支持通配符，也无法禁用此检查。允许列表绝不会放宽 TLS 要求：签发方为 `https://` 时，`http://` 端点仍会被拒绝。不要仅因登录错误中提到了某个主机就将其添加到允许列表；出现该错误恰恰说明检查在正常工作。

## 撤销如何生效

组访问控制基于缓存的 ID 令牌进行校验，因此在 IdP 中将用户从 `required_group` 中移除 (或禁用该用户) 后，变更会在该用户下一次刷新令牌时生效：最迟在当前 ID 令牌过期时生效，外加 120 秒的时钟偏差容限。如果需要快速撤销，请为此客户端设置较短的 ID 令牌有效期。

如果刷新响应中未返回新的 ID 令牌，系统会继续使用缓存的 ID 令牌，但最长只能沿用至该令牌 `exp` 之后 24 小时；超过此时限后，即使 IdP 仍接受该 refresh token，用户也必须重新登录。

### 运行中会话的重新验证

Devin Desktop (或内置智能体的任何客户端) 运行期间，智能体每 **60 秒**重新检查一次登录状态，并至少每 **15 分钟**向 IdP 强制刷新一次令牌。如果检查失败，智能体会记录一条 code 为 `IDP` 的 `sso_revalidation_failed` 审计事件，随即停止接受新任务并关闭；用户必须重新登录并重启。

## 审计内容

SSO (单点登录) 活动会以 `auth` 事件的形式写入本地[审计日志](/zh/federal/air-gapped/on-disk-formats#audit-log)。凡是在发生时持有已验证令牌的事件，都会在 `actor` 字段中记录 SSO 身份信息 (`sso_issuer`、`sso_subject`、`sso_user`、`sso_groups`) 。

| `action` | 触发时机 |
| - | - |
| `sso_login` | 登录完成 (通过 `devin auth login` 或 Desktop) 。 |
| `sso_login_denied` | IdP 响应后登录被拒绝：缺少所需的组 (`error_code: "IDP004"`) ，或返回的 ID 令牌未通过验证或缺失 (`IDP005`) 。`actor` 中记录的是 IdP 所确认的身份；不会缓存任何内容。 |
| `sso_refresh` | 已通过 IdP 刷新令牌。 |
| `sso_logout` | 执行 `devin auth logout` 或在 Desktop 中退出登录。 |
| `sso_revalidation_failed` | 运行中的智能体失去登录状态，正在关闭；`error_code` 中包含相应的 `IDP` 错误码。 |

## IDP 错误代码

| 代码 | 含义 | 处理方法 |
| - | - | - |
| `IDP001` | 认证文件无法读取或无效。 | 修复 `/etc/devin/auth.json` (或 `DEVIN_AUTH_FILE` 指定的路径) ；错误消息会指出有问题的字段。 |
| `IDP002` | 没有已配置签发方的缓存登录信息。 | 运行 `devin auth login`。 |
| `IDP003` | 登录无效或已过期，且无法静默刷新 (包括 IdP 返回 `invalid_grant` 响应的情况) 。 | 重新运行 `devin auth login`。 |
| `IDP004` | 已登录，但该用户不是 `required_group` 的成员。 | 在 IdP 中将该用户添加到该组，或更正 `required_group` / `groups_claim`。 |
| `IDP005` | 无法连接 IdP，或 IdP 返回了错误。 | 检查网络连通性、TLS 信任 (`DEVIN_CA_BUNDLE`) 以及 IdP 自身的日志。 |
| `IDP006` | 本地 SSO (单点登录) 令牌缓存无法读取、格式错误，或并非当前用户私有。 | 运行 `devin auth login`；重新登录会重写该缓存。 |

完整的错误代码目录请参阅[错误代码](/zh/federal/air-gapped/error-codes)页面。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.