ByteNoteByteNote
ChatGPT OAuth:让订阅额度跑自建 Agent
字

字节笔记本

2026年10月24日 · 约 12 分钟读完

ChatGPT OAuth:让订阅额度跑自建 Agent

API中转
¥120

Agent SDK 概览

本文是《从 0 写 Agent》系列第 39 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「ChatGPT OAuth:用订阅额度跑 Agent」。

agents-server 有个特别的能力,用 ChatGPT 订阅账号跑 Agent,不烧 API 额度。这靠 OAuth 2.1 + PKCE 实现。

这一章拆这个 OAuth 流程,本地回调服务器、postMessage 弹窗通信、token 自动刷新。这是 Part VI 的收尾。


引子:为什么用 ChatGPT 订阅

OpenAI 有两种计费:

  • API,按 token 付费,每百万 token 几美元
  • ChatGPT 订阅(Plus/Pro/Max),月费固定,含一定使用额度

如果你有 ChatGPT Pro 订阅(月费固定),用它跑 Agent 比 API 便宜得多,尤其对重度使用者。

agents-server 的 ChatGPT OAuth 功能,就是让你的 Agent 用 ChatGPT 订阅额度,而不是 API key。它复用了 Codex CLI 的 OAuth 流程(同样的 client_id)。


Claude Code

OAuth 2.1 + PKCE 流程

go
// bridge/chatgpt_oauth.go:21
const (
    clientID    = "app_EMoamEEZ73f0CkXaXp7hrann"  // Codex CLI 的公开 client_id
    authorizeURL = "https://auth.openai.com/authorize"
    tokenURL     = "https://auth.openai.com/token"
    redirectURI  = "http://localhost:1455/auth/callback"
)

完整流程(chatgpt_oauth.go:63):

text
1. 前端点"用 ChatGPT 登录"
2. 后端 StartLogin:
   a. 生成 state(32 字节随机)和 PKCE verifier(64 字节)
   b. 算 challenge = base64(SHA256(verifier))
   c. 起一个临时 HTTP 服务器监听 localhost:1455(回调)
   d. 拼 authorize URL(含 challenge、state)
   e. 返回 URL 给前端
3. 前端弹窗打开 authorize URL
4. 用户在弹窗里登录 OpenAI 并授权
5. OpenAI 重定向到 localhost:1455/auth/callback?code=xxx&state=xxx
6. 临时服务器接收回调:
   a. 校验 state(防 CSRF)
   b. 用 code + verifier 换 token(token endpoint)
   c. 存 token 到数据库
   d. 返回 HTML,用 postMessage 通知弹窗关闭
7. 弹窗关闭,前端知道登录完成

关键设计一:本地回调服务器

go
// chatgpt_oauth.go:119(概念)
func listenCallback(redirectURI) {
    // 在 localhost:1455 起一个临时 HTTP 服务器
    // (端口被占则退 1457)
    server := &http.Server{Addr: ":1455", Handler: mux}
    go server.ListenAndServe()
}

为什么起本地服务器当回调? 因为 OAuth 回调必须是 http://(重定向到 localhost)。不能回调到你的 Agent 平台(那要公网域名 + HTTPS)。

临时服务器在登录期间存在,登录完就关。 不影响主 gin server(在 8080 端口)。这是"按需启动"的精简设计。


关键设计二:postMessage 弹窗通信

go
// chatgpt_oauth.go:389(概念)
func callbackHTML(status string) string {
    return `<script>
        window.opener.postMessage({type: 'chatgpt-oauth-done', status: '` + status + `'}, '*');
        window.close();
    </script>`
}

OAuth 回调发生在弹窗里(前端用 window.open 打开的)。 弹窗完成授权后,要通知"父窗口"(主界面)登录完了。

window.opener.postMessage,弹窗给打开它的窗口发消息。父窗口监听这个消息,知道"登录完成",关闭弹窗,刷新登录状态。

这是浏览器里"弹窗 ↔ 主窗口"通信的标准方式。 比"轮询后端状态"简单。


关键设计三:PKCE 防授权码拦截

go
// chatgpt_oauth.go:384
func pkceChallenge(verifier string) string {
    hash := sha256.Sum256([]byte(verifier))
    return base64.RawURLEncoding.EncodeToString(hash[:])
}

PKCE 流程:

  1. 后端生成随机 verifier(64 字节)
  2. 算 challenge = base64(SHA256(verifier))
  3. 授权时发 challenge(不给 verifier)
  4. 换 token 时发 verifier

即使攻击者截获了授权码(callback URL 里的 code),他没有 verifier,换不到 token。 因为换 token 时要同时提供 code + verifier,OpenAI 会验证 SHA256(verifier) == challenge。

PKCE 是 OAuth 2.1 的强制要求,所有公共客户端(不能安全存 secret 的,如桌面/移动应用)必须用 PKCE。


Token 自动刷新

go
// chatgpt_oauth.go:217(概念)
func GetCredentials(agentConfigID) {
    token := loadFromDB(agentConfigID)
    // 过期前 60 秒自动 refresh
    if time.Until(token.ExpiresAt) < 60*time.Second {
        token = refreshToken(token.RefreshToken)
        saveToDB(token)
    }
    return token.AccessToken
}

Access token 会过期(通常 1 小时)。 agents-server 在过期前 60 秒自动用 refresh token 换新 access token。用户无感知,不会跑到一半 token 过期失败。

从 JWT 提取 account_id

go
// chatgpt_oauth.go:249
func decodeAccountID(accessToken string) string {
    // access token 是 JWT,payload 里有 account_id
    payload := parseJWT(accessToken)
    return payload["https://api.openai.com/auth.chatgpt_account_id"]
}

ChatGPT 的 API 要带 account_id header。account_id 嵌在 access token(JWT)的 payload 里。agents-server 解析 JWT 提取它。


设计权衡:per-agent 登录

agents-server 的 ChatGPT OAuth 是 per-agent 的,每个 AgentConfig 可以独立登录不同 ChatGPT 账号:

go
// 存在 agent_configs 表的 chatgpt_token 列
// 每个 agent config 有自己的 token

为什么 per-agent? 因为不同 Agent 可能用不同账号,你的个人 Agent 用你的 ChatGPT 账号,团队共享 Agent 用团队账号。per-agent 让每个 Agent 独立计费、独立额度。


小结:Part VI 回顾

第 34-39 章,把所有组件组装成完整平台:

章节组件
34 总览四层架构 + 手动 DI
35 数据库10 表 + JSON 列 + 泛型 CrudStore
36 WebSocket三层流式管道
37 HITLpending map + 端到端审批
38 前端go:embed + 零构建
39 OAuthPKCE + 本地回调 + postMessage

agents-server 是 agents-go SDK 的"完整应用范本",从 SDK 到产品。 ChatGPT OAuth 是它的一个亮点功能,也是 OAuth 桌面应用的标准实现。


下一篇

第 40 章,结语:你现在已经能从 0 写 Agent 了。Part VII 的最后一章,回顾全书,总结 Agent 运行时的 7 个核心设计模式,以及从 agents-go 到生产环境还差什么。


本文是《从 0 写 Agent》系列第 39 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

相关文章

分享: