opencodex 多账户 OAuth 认证体系:从 auth.json 多账户存储到 GUI 账户切换的完整实现
opencodex 多账户 OAuth 认证体系从 auth.json 多账户存储到 GUI 账户切换的完整实现【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址: https://gitcode.com/gh_mirrors/ope/opencodexopencodex 作为 OpenAI Codex / Claude Code 的通用 Provider 代理其 OAuth 认证层在 260706 迭代中完成了多账户化改造multiauth同一 Provider 下可以同时登录多个账户运行时只使用“激活账户”的凭据并在 GUI 上提供账户下拉切换、添加、删除能力。本文以devlog/_fin/260706_provider-multiauth的开发记录为主线结合src/oauth源码与测试完整讲解数据模型、迁移策略、解析器、Token Guardian、管理 API 与 GUI 交互并如实说明各 Provider 的适用边界。功能背景与设计目标在 multiauth 之前~/.opencodex/auth.json中每个 Provider 只保存一份 OAuth 凭据单槽位。用户在同一 Provider 下拥有多个账号时只能反复登出再登录且后台账户的刷新令牌无人维护容易出现“登录中途失效”开发日志中称为“로그인 안풀리는”问题。multiauth 单元的明确目标见 000_plan.md是auth.json每个 Provider 保存 N 个账户支持激活账户切换GUI Provider 卡片出现细条账户下拉框点击即激活请求只使用激活账户本单元不做跨账户 429 自动轮换轮换属于后续单元显式非目标跨账户 429 自动轮换、按账户拉取配额、API-key 池、Codex 池改动、usage-log 的 accountId 归属。范围排除Codex / ChatGPT 的 passthrough 池已有独立体系codex-accounts.json Codex Auth 页multiauth 不重复实现。数据模型与迁移策略新形状ProviderAccountSet每个 Provider 在auth.json中的值从单个凭据对象升级为账户集合types.tsexport interface ProviderAccount { id: string; // 追加时一次性生成的稳定短 id轮换后不再重推 alias?: string; // 用户自定义显示名不参与鉴权 credential: OAuthCredentials; // access / refresh / expires / email / accountId ... needsReauth?: boolean; // 终态刷新失败invalid_grant / reused / revoked addedAt?: number; } export interface ProviderAccountSet { activeAccountId: string; // 当前激活账户请求只使用它 selectionRevision?: string; // 选择代数旧仓库无此字段 accounts: ProviderAccount[]; }持久化形状为{ provider: { activeAccountId, accounts: [...] } }示例000_plan.md{ anthropic: { activeAccountId: a1b2, accounts: [ { id: a1b2, credential: { access: ..., refresh: ..., expires: 0, email: xy.z }, needsReauth: false, addedAt: 0 } ] } }账户 id 派生规则账户 id 必须“对同一凭据稳定、对不同凭据可区分”。src/oauth/store.ts中newAccountId()使用sha256(accountId ?? email ?? refresh)取 128 bit32 个 hex 字符追加时若与既有账户冲突则加-1、-2后缀distinctAccountId。注意id 只在追加时生成一次并持久化轮换刷新令牌后不再重推——因为getAccountCredential依赖 id 定位账户若每次刷新都重新派生 id会导致刷新结果写入失败、令牌静默丢失。旧数据兼容legacy 归一化 一次性降级备份加载时若某 Provider 的值是旧的单凭据对象含access字符串normalizeAccountSet会将其包装为{ activeAccountId: derived, accounts: [一条] }登录不丢失持久化一律写新形状加载器对两种形状永久兼容降级安全网首次以新形状覆盖仍含 legacy 条目的文件前会写入一次性备份auth.json.pre-multiauth0600 权限防止旧版 opencodex 静默丢弃新形状后无法恢复刷新令牌实现见 store.ts 的backupLegacyOnce。单槽位例外chatgpt始终单槽位codex-auth-api把它当作 Codex 池登录的临时槽位池子自己有codex-accounts.json账本因此saveCredential(chatgpt, ...)总是整体替换SINGLE_SLOT_PROVIDERS。无身份字段无 accountId/email的凭据普通登录时替换激活槽位避免轮换刷新令牌制造重复账户显式“添加账户”登录则保留旧槽位并追加preserveIdentityless选项。存储层mutateStore 与多账户 APIsrc/oauth/store.ts约 1108 行是 multiauth 的核心存储实现全部写操作走in-process 串行化mutateStore内部先获取auth.store.lock文件锁30s stale在队列中执行 load → mutate → persist避免“guardian 刷新非激活账户”与“用户切换激活账户”并发造成的丢失更新。跨进程竞态被接受单代理假设。存储公开 APIstore.tsAPI语义getCredential(provider)返回激活账户凭据请求路径用saveCredential(provider, cred)同身份 upsert替换凭据并清除 needsReauth并激活新身份追加无身份替换激活槽removeCredential(provider)仅删除激活账户剩余账户提升第一个否则删除 Provider 键listAccounts(provider)列出全部账户getAccountSet(provider)获取账户集合getAccountCredential(provider, accountId)/getAccountCredentialWithStatus读取指定账户凭据后者一次性带回 needsReauth 标志省一次全文件解析saveAccountCredential(provider, accountId, cred)为指定账户持久化刷新后的凭据不动 activeAccountIdguardian 专用路径setActiveAccount(provider, accountId)切换激活账户removeAccount(provider, accountId)删除指定账户激活账户被删则提升下一个markAccountNeedsReauth(provider, accountId, flag)标记终态刷新失败upsertCredentialByIdentity(provider, cred)原子插入/更新身份凭据导入器用返回 inserted/updatedcommitOAuthAccountSelection/captureOAuthAccountSelection配合selectionRevision实现选择一致性只有选择确实变化或手动重申才推进代数持久化后通过publishAccountSelection发布事件GUI 可订阅实时刷新。账户感知的认证解析层src/oauth/index.ts是解析层核心单飞锁升级single-flight Map 的键从provider改为provider \u0000 accountId每个 (provider, account) 独立去重刷新getValidAccessToken(provider)解析激活账户后委托给新的getValidAccessTokenForAccount(provider, accountId)读取getAccountCredential过期则发起单飞刷新刷新成功 →saveAccountCredential(provider, accountId, merged)——先落盘再返回 access轮换安全新 refresh token 先写盘与 auth2api manager 的实践一致刷新时保留 identity 字段刷新失败分类响应体含invalid_grant/refresh_token_reused/expired大小写不敏感视为终态→markAccountNeedsReauth(provider, accountId, true)并抛OAuthLoginRequiredError其余错误按瞬时错误重抛kiro 本地 CLI 导入回退仅在accountId activeAccountId时执行避免后台账户被错误导入forceLoginxai / anthropic 传importLocal: off跳过本地 CLI 令牌导入、走真实 OAuth 流使“添加第二个账户”不会复用浏览器会话的第一个账户antigravity 强制select_account提示cursor 的第二个参数是轮询间隔数字不能盲传 opts。Token Guardian为每个账户保活src/oauth/token-guardian.ts负责后台主动刷新遍历每个 Provider 的全部账户listAccounts跳过needsReauth账户终态只有重新登录能修复调用getValidAccessTokenForAccount退避键升级为oauth:provider:accountIdneedsReauth 账户获得永久退避不再反复锤击死掉的刷新令牌安全设计guardian 只触碰有效 refreshPolicy 为proactive的 Provider且全局开关config.tokenGuardian.enabled默认关闭——默认无新增后台流量保活语义呼应“登录不失效”诉求非激活账户仅当 Provider 为 proactive 策略时才由 guardian 保活。Anthropic 默认保持disabledToS 风险其第二个账户只能存活到其刷新令牌自然过期为止。管理 API/api/oauth/accountssrc/server.tsOAuth 端点块新增/修改的接口详见 030_api.mdGET /api/oauth/accounts?providerx→{ activeAccountId, accounts: [{ id, email(掩码), active, needsReauth, expiresAt, health... }] }未知 Provider 返回 400PUT /api/oauth/accounts/activebody{ provider, accountId }→ 切换激活账户账户不存在返回 404成功后使clearProviderQuotaCache()使配额缓存失效并清除该 Provider 的 live-model 缓存配额条与模型列表立即反映新账户DELETE /api/oauth/accounts?providerxidy→ 删除指定账户删除最后一个账户时同时clearLoginState(provider)POST /api/oauth/login接受可选{ addAccount: true }→ 以forceLogin: true启动登录流允许在浏览器中选择新身份原有单账户登录行为不变POST /api/oauth/logout不变删除激活账户。安全不变量邮箱经maskEmail掩码任何响应不含 tokenOAuthAccountSummary与OAuthAccessSnapshot为手写白名单结构从构造上保证机密不出响应。测试tests/oauth/oauth-accounts-api.test.ts验证了GET 列表邮箱被掩码且不含 tokenraw.includes(t1) false、needsReauth 账户投影reauth_required健康状态与ocx login anthropic修复动作、PUT 切换后activeAccountId变更、未知账户 404 / 未知 Provider 400、DELETE 删除激活账户后自动提升剩余账户以及切换 Antigravity 账户后 account-scoped live-model 缓存被清空、在途的旧账户模型发现被丢弃。GUIProvider 卡片上的账户下拉gui/src/pages/Providers.tsx与gui/src/styles.css实现了移除.provider-quota顶部分割线对应“중간선 없애고”需求保留 paddingOAuth 卡片新增细条Accounts (N)折叠行chevron 随展开旋转展开后列出账户圆点 掩码邮箱 激活徽标点击非激活账户 →PUT /api/oauth/accounts/active→ 刷新账户列表 配额 通知每账户提供删除图标末尾 Add account行 →POST /api/oauth/login { addAccount: true }复用既有 loginOAuth 轮询提取为接受 addAccount 标志仅authMode oauth的卡片显示跳过 forward/passthrough 与 chatgpt——Codex 池独立单账户卡片也显示作为添加第二个账户的入口i18n 键en/ko/zhprov.accountsAccounts {n}、prov.accountActive、prov.accountSwitch、prov.accountAdd、prov.accountRemoved、prov.accountSwitched无邮箱的账户如 cursor回退显示短账户 idi18nprov.accountNoLabel。GUI 构建验证tsc -b干净、vite build 通过、浏览器截图确认分割线消失、下拉可打开并切换 Active 徽标、390px 移动宽度无溢出。适用边界与诚实限制multiauth 不是对所有 Provider 一视同仁开发日志的 D 总结050_done.md明确划定了边界有效多账户 Providerxai、anthropic、google-antigravity凭据含稳定身份保持单账户kimi、kiro、cursor刷新令牌轮换、凭据无稳定身份派生 id 会把同一人拆成多个账户。Kiro 第二个账户不支持import-first 流程。不过 kimi 会从 JWT 提取user_id/sub作为 accountId、cursor 提取 JWTsub二者在 multiauth 下可追加带身份的独立账户Anthropic 后台账户保活受限默认 refreshPolicy 为 disabled第二个账户只存活到刷新令牌自然过期本单元不含跨账户 429 自动轮换后续单元设计草稿cooldown Retry-After 下次请求切换跨进程存储竞态被接受单代理假设。验证方式与运行说明开发日志记录的质量门禁可复现验证# 类型检查根目录 GUI bun x tsc --noEmit cd gui bun x tsc -b # 全量测试含 oauth-store-multi、oauth-accounts-api、token-guardian 新断言 bun test ./tests/ # 隐私扫描确认无密钥泄露 bun run privacy:scan # 运行时冒烟隔离 OPENCODEX_HOME 启动代理GET 掩码账户列表、PUT 切换、DELETE 提升存储层测试位于 tests/oauth/oauth-store-multi.test.ts覆盖legacy→新形状往返、新身份追加并激活、同身份替换、setActiveAccount 切换、激活账户删除后提升、末位删除清除 Provider、needsReauth 持久化、非法条目丢弃管理 API 测试位于 tests/oauth/oauth-accounts-api.test.ts。关闭记录999_closed.md确认commit 12aff01 交付多账户 OAuth 存储与 API-key 池支持最新全量套件 1555 通过 / 0 失败。使用提示auth.json位于~/.opencodex/可通过OPENCODEX_HOME环境变量重定向GUI 仪表板默认监听 10100 端口CLI 侧删除账户可用ocx login provider重新登录、管理 API DELETE 精确删除指定账户。【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址: https://gitcode.com/gh_mirrors/ope/opencodex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

3个致命坑:搞懂呈现的拼音,面试必问不再丢分

3个致命坑:搞懂呈现的拼音,面试必问不再丢分

3个致命坑:搞懂呈现的拼音,面试必问不再丢分 刚复制的代码直接跑通?那是运气好。更多时候,你盯着控制台里满屏的 UnicodeEncodeError…

2026/9/25 1:29:29 阅读更多 →
时钟英语速查手册:3分钟搞懂底层逻辑,面试不再卡壳

时钟英语速查手册:3分钟搞懂底层逻辑,面试不再卡壳

时钟英语速查手册:3分钟搞懂底层逻辑,面试不再卡壳 面试时考官问起时钟同步原理,你答不上来?别慌,这份时钟英语速查手册能救急。很多开发者把时钟当黑盒,只会调 API,真问到底层机制就露怯。 核心痛点直击 :你背了 NTP…

2026/9/23 20:36:56 阅读更多 →
3分钟搞懂微信打飞无敌模式源码,从入门到精通避坑指南

3分钟搞懂微信打飞无敌模式源码,从入门到精通避坑指南

3分钟搞懂微信打飞无敌模式源码,从入门到精通避坑指南 版本升级后 API 全变了?别慌,这不是你的代码烂,是底层机制在变。很多开发者在接入微信相关功能时,一遇到接口变更就抓瞎,以为需要推倒重来。其实,只要吃透了核心逻辑,从入门到精通只需要理…

2026/9/23 20:36:56 阅读更多 →

最新新闻

QQ截图钉在桌面怎么用?让截图悬浮置顶,学习办公效率翻倍

QQ截图钉在桌面怎么用?让截图悬浮置顶,学习办公效率翻倍

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 1:30:31 阅读更多 →
ESP32上跑WASM:硬件访问的边界与宿主函数借道方案

ESP32上跑WASM:硬件访问的边界与宿主函数借道方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 1:30:31 阅读更多 →
基于RK3576的交互大屏商显主板方案解析

基于RK3576的交互大屏商显主板方案解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 1:30:31 阅读更多 →
华为AP4050DN FIT转FAT刷机教程:console线+TFTP自救指南

华为AP4050DN FIT转FAT刷机教程:console线+TFTP自救指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 1:30:31 阅读更多 →
大模型驱动的知识图谱构建与RAG落地实践

大模型驱动的知识图谱构建与RAG落地实践

简介:本资源是一份面向企业架构师、知识管理工程师与AI技术决策者的专业级解决方案PPT,聚焦AI大模型与知识管理系统深度融合的落地路径。内容系统覆盖知识图谱与大模型协同架构、认知智能双引擎设计、动态知识抽取与自演进图谱构建、多模态数据统一表征&…

2026/9/25 1:30:31 阅读更多 →
Microchip Studio 7 烧录 AVR 单片机:熔丝位配置与避坑指南

Microchip Studio 7 烧录 AVR 单片机:熔丝位配置与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 1:29:31 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →