在 CI/CD 中维护 Codex 账号认证(高级)

使用 Codex 内置的刷新流程,让 auth.json 在受信任的 CI/CD runner 中持续可用

本指南说明如何在可信的 CI/CD 运行器上维持 ChatGPT 托管的 Codex 认证,而无须自行调用 OAuth token endpoint。

自动化任务应优先使用 API key 认证。只有当工作流确实需要以你的 Codex 账号身份运行时,才使用本指南中的方案。

整体流程如下:

  1. 在可信设备上运行一次 codex login,生成 auth.json
  2. 将该文件放到运行器上。
  3. 正常运行 Codex。
  4. 当会话过期时,由 Codex 自动刷新。
  5. 保存刷新后的 auth.json,供下一次运行使用。

这是一种面向企业及其他可信私有自动化环境的高级工作流。对于大多数 CI/CD 任务,API key 仍是推荐方案。

工作原理

Codex 本身已经能够刷新由 ChatGPT 托管的会话。

以当前的开源客户端为准:

  • Codex 会从 auth.json 加载本地认证缓存。
  • 如果 last_refresh 距今超过约 8 天,Codex 会先刷新令牌集,再继续运行。
  • 刷新成功后,Codex 会把新令牌和新的 last_refresh 写回 auth.json
  • 如果请求收到 401,Codex 也有内建的刷新并重试流程。

因此,受支持的 CI/CD 策略不是“自行调用刷新 API”,而是“运行 Codex,并持久化更新后的 auth.json”。

适用条件

只有同时满足以下条件时,才使用本指南:

  • 你需要 ChatGPT 托管的 Codex 认证,而不是 API key。
  • 远程运行器无法执行 codex login
  • 运行器属于可信的私有基础设施。
  • 你能够在多次运行之间保存刷新后的 auth.json
  • 一份 auth.json 只由一台机器或一组串行执行的任务使用。

本指南适用于 Codex 管理的 ChatGPT 认证(auth_mode: "chatgpt")。

它不适用于:

  • API key 认证。
  • 由宿主应用管理外部 token 的集成(auth_mode: "chatgptAuthTokens")。
  • Codex 之外的通用 OAuth 客户端。

如果凭据保存在操作系统钥匙串中,请先改用文件存储。参见凭据存储

仅初始化一次 auth.json

在能够通过浏览器登录的可信设备上执行以下步骤:

  1. 配置 Codex,将凭据保存到文件中:
cli_auth_credentials_store = "file"
  1. 运行:
codex login
  1. 验证该文件是否为 ChatGPT 托管认证:
AUTH_FILE="${CODEX_HOME:-$HOME/.codex}/auth.json"

jq '{
  auth_mode,
  has_tokens: (.tokens != null),
  has_refresh_token: ((.tokens.refresh_token // "") != ""),
  last_refresh
}' "$AUTH_FILE"

只有满足以下条件时才继续:

  • auth_mode"chatgpt"
  • has_refresh_tokentrue

然后,将 auth.json 的内容存入 CI/CD 密钥管理系统,或把文件复制到可信且可持久化的运行器。

推荐方案:使用自托管运行器的 GitHub Actions

最简单的全自动方案,是使用带持久化 CODEX_HOME 的自托管 GitHub Actions 运行器。

这一方案具有以下优势:

  • 运行器可以在多次任务之间保留磁盘上的 auth.json
  • Codex 可以直接刷新原文件。
  • 后续任务会自动使用刷新后的令牌。
  • 原始 secret 只在初始化或重新写入凭据时需要。

关键在于:仅当 auth.json 不存在时才写入初始文件。如果每次运行都用原始 secret 覆盖该文件,就会丢掉 Codex 上一次写入的刷新结果。

下面是一份定时工作流示例:

name: Keep Codex auth fresh

on:
  schedule:
    - cron: "0 9 * * 1"
  workflow_dispatch:

jobs:
  keep-codex-auth-fresh:
    runs-on: self-hosted
    steps:
      - name: Bootstrap auth.json if needed
        shell: bash
        env:
          CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
        run: |
          export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
          mkdir -p "$CODEX_HOME"
          chmod 700 "$CODEX_HOME"

          if [ ! -f "$CODEX_HOME/auth.json" ]; then
            printf '%s' "$CODEX_AUTH_JSON" > "$CODEX_HOME/auth.json"
            chmod 600 "$CODEX_HOME/auth.json"
          fi

      - name: Run Codex
        shell: bash
        run: |
          codex exec --json "Reply with the single word OK." >/dev/null

该工作流会:

  • 在首次运行时写入 auth.json
  • 在后续运行中复用同一文件。
  • 当缓存会话达到过期条件时,在正常的 codex exec 步骤中由 Codex 自动刷新。
  • 将刷新后的文件保留在磁盘上,供下一次工作流运行使用。

当前开源客户端会在大约 8 天后把会话视为已过期,因此每周运行一次通常就足够。

临时运行器:恢复文件、运行 Codex,再持久化更新后的文件

如果使用 GitHub 托管运行器、GitLab 共享运行器或其他临时环境,每个任务结束后,运行器的文件系统都会被清除。此时需要完成一次完整的往返流程:

  1. 从安全存储中恢复当前的 auth.json
  2. 运行 Codex。
  3. 将更新后的 auth.json 写回安全存储。

GitHub Actions 的通用结构如下:

name: Run Codex with managed auth

on:
  workflow_dispatch:

jobs:
  codex-job:
    runs-on: ubuntu-latest
    steps:
      - name: Restore auth.json
        shell: bash
        run: |
          export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
          mkdir -p "$CODEX_HOME"
          chmod 700 "$CODEX_HOME"

          # Replace this with your secret manager or secure storage command.
          my-secret-cli read codex-auth-json > "$CODEX_HOME/auth.json"
          chmod 600 "$CODEX_HOME/auth.json"

      - name: Run Codex
        shell: bash
        run: |
          codex exec --json "summarize the failing tests"

      - name: Persist refreshed auth.json
        if: always()
        shell: bash
        run: |
          # Replace this with your secret manager or secure storage command.
          my-secret-cli write codex-auth-json < "$CODEX_HOME/auth.json"

关键要求是:回写步骤必须保存 Codex 在本次运行中生成的刷新后文件,而不是最初用于初始化的原始文件。

不需要单独的刷新命令

任何一次正常的 Codex 运行都可以刷新会话。

因此有两种合适的做法:

  • 让现有的 CI/CD Codex 任务自然刷新该文件。
  • 如果实际任务运行得不够频繁,可以增加一个轻量的定时维护任务,如上面的 GitHub Actions 示例。

会话达到过期条件后,第一次运行 Codex 时就会刷新 auth.json

关键运行规则

  • 每台运行器或每组串行工作流使用一份独立的 auth.json
  • 不要让并发任务或多台机器共享同一文件。
  • 不要在每次运行时用原始文件覆盖持久化运行器上已经刷新的文件。
  • 不要把 auth.json 存入仓库、日志或公开的产物存储。
  • 如果内建刷新不再有效,请在可信设备上重新生成认证文件。

刷新失效时的处理方法

该流程可以减少人工操作,但不能保证同一会话永久有效。

出现以下情况时,请用新的 auth.json 重新初始化运行器:

  • Codex 开始返回 401,并且运行器无法继续刷新。
  • refresh token 已被撤销或过期。
  • 另一台机器或并发任务率先轮换了令牌。
  • 安全存储的往返流程失败,恢复了旧文件。

重新初始化:

  1. 在可信设备上运行 codex login
  2. 替换 CI/CD 中保存的 auth.json
  3. 让下一个运行器任务继续使用 Codex 的内建刷新流程。

验证运行器是否持续维护会话

检查运行器是否仍保存着托管认证所需的令牌,以及 last_refresh 是否存在:

AUTH_FILE="${CODEX_HOME:-$HOME/.codex}/auth.json"

jq '{
  auth_mode,
  last_refresh,
  has_access_token: ((.tokens.access_token // "") != ""),
  has_id_token: ((.tokens.id_token // "") != ""),
  has_refresh_token: ((.tokens.refresh_token // "") != "")
}' "$AUTH_FILE"

对于持久化运行器,同一文件应在多次运行之间始终存在。对于临时运行器,请确认回写步骤保存的是上一次任务生成的更新后文件。

源码参考

如需在开源客户端中验证上述行为: