Skip to content

feat(openai-reauth): 新增 OpenAI OAuth 重新授权独立流程#292

Closed
ranger1112 wants to merge 21 commits into
QLHazyCoder:masterfrom
ranger1112:feat/openai-reauth-flow
Closed

feat(openai-reauth): 新增 OpenAI OAuth 重新授权独立流程#292
ranger1112 wants to merge 21 commits into
QLHazyCoder:masterfrom
ranger1112:feat/openai-reauth-flow

Conversation

@ranger1112
Copy link
Copy Markdown

Summary

  • 新增 openai-reauth flow:4 步 OAuth PKCE 重新授权流程(准备 → 提交邮箱 → 获取验证码 → 捕获回调换 Token)
  • 配套 sidepanel UI:批量模式、文件导入/导出、进度跟踪、mailProvider 选择
  • 批量协调器:START_REAUTH_BATCH 消息驱动,自动串行处理账号队列
  • OAuth 同意页主动点击:debugger + content script 双策略编排,自动点击「继续」按钮
  • 封禁账号秒级检测:DETECT_ACCOUNT_BANNED 消息 + chrome.scripting 降级双通道,识别后自动跳过

Key changes

File Change
flows/openai-reauth/** 全新 reauth flow 模块(步骤、OAuth 客户端、批量运行器、账号校验器)
flows/openai/content/openai-auth.js 新增 DETECT_ACCOUNT_BANNED 等消息类型支持
background.js 注册 reauth 节点、批量协调器、消息路由
sidepanel/sidepanel.js reauth 批量模式 UI、文件选择器、进度展示
sidepanel/sidepanel.html reauth 面板 HTML 结构

Test plan

  • 单账号 reauth 完整流程(正常账号)
  • 批量 reauth(3+ 账号混合成功/失败/封禁)
  • 封禁账号秒级检测跳过(不超时等待)
  • OAuth 同意页自动点击(debugger + content script 双策略)
  • 文件导入/导出整文件 JSON 格式兼容

ranger1112 and others added 18 commits May 28, 2026 17:00
- flows/openai-reauth/: 独立 flow definition + workflow + mail-rules
- background/oauth-client.js: PKCE 生成 + URL 构造 + Token 交换 (fetchImpl 注入)
- background/cookie-cleanup.js: 清 OpenAI/ChatGPT cookies (复用 step1 模式)
- background/steps/: 4 个 step executor (prepare → submit-email → fetch-code → capture-callback)
- 注册到 flows/index.js + manifest.json + background.js (importScripts/mailRuleRegistry/stepExecutorsByKey)
- 复用 oauth-login content handler、pollFlowVerificationCode、FILL_CODE
- 测试: oauth-client (14) / mail-rules (10) / cookie-cleanup (6) / capture-callback (8) = 38 个新测试
- 全量回归 1307/1307 通过

注: sidepanel UI 留待下一期 (需要 message-router 与 sidepanel 主 UI 配合升级)
- .omc/: oh-my-claudecode 状态目录
- /tmp/: 本地实验脚本与含真实 token 的账号 JSON 临时存放区,不入库
- flows/openai-reauth/index.js: settingsGroups 加 reauth-input + baseGroups 追加
- flows/openai-reauth/reauth-account-validator.js: 独立校验函数模块(新建,可单测)
- sidepanel/sidepanel.html: 新增 row-reauth-account-json 和 row-reauth-result(含 textarea/pre/复制按钮)
- sidepanel/sidepanel.css: textarea / readonly pre 样式
- sidepanel/sidepanel.js: DOM 常量、ensurePendingReauthAccount、renderReauthResultAccount、step 按钮 payload 注入、复制按钮绑定
- background/message-router.js: EXECUTE_NODE case 内提取 reauthInputAccount 写入 state
- tests/openai-reauth-oauth-client.test.js: 用 node:crypto webcrypto 替代 globalThis.crypto,修复 Node 18 sandbox
- tests/openai-reauth-validate-account-json.test.js: 13 个校验函数边界测试(新建)

reauth 测试 50/50 通过;全量回归 1316/1320,4 个失败是 master 原本的 kiro Node 18 sandbox 问题,与 P2 无关
1. row 的 hidden 属性会被 [hidden] !important CSS 锁死,改用 inline style=display:none
   - applyFlowSettingsGroupVisibility 用 style.display 切换,无法盖过 [hidden] !important
2. NODE_STATUS_CHANGED handler 完成时没刷新 reauthResultAccount
   - step 4 跑完后 sidepanel 不会渲染新 JSON 到结果区

review 后端到端集成验证全通,reauth 50/50 测试全绿
- reauth-account-validator.js: 重写为 parseAccountsFromJson + buildResolvedAccount
  支持单账号 / accounts 数组 / sub2api 整文件三种输入形态
  不再要求 JSON 含 mailProvider 字段(改由 UI 注入)
- sidepanel.html: 加邮箱来源下拉(7 个 provider)+ 多账号下拉选择器
- sidepanel.js: 改 ensurePendingReauthAccount 为多账号选择 + provider 注入
  account 选择器仅在 accounts.length > 1 时显示
- tests: 重写 15 个用例覆盖整文件 / 单账号 / 多账号 / 边界

reauth 测试 52/52 通过
复盘:截图显示 sidepanel 已识别账号但 step 1 仍报"缺少待重新授权的账号 JSON"
根因:用户点的是"自动"按钮走 AUTO_RUN 消息,本仙女只在 step 按钮 (EXECUTE_NODE)
路径上注入了 reauthInputAccount

修复:
- sidepanel.js: AUTO_RUN 启动前若 activeFlowId === 'openai-reauth'
  调 ensurePendingReauthAccount() 解析当前选中账号 + provider 注入 payload
  校验失败时恢复按钮状态并中止
- background/message-router.js: AUTO_RUN case 内提取 reauthInputAccount
  写入 session state(与 EXECUTE_NODE case 同步行为)
师兄反馈三件事:
1. JSON 输入改为文件选择器(后续批量也是导入文件)
2. 不需要邮箱服务区(reauth 自己有邮箱来源下拉,凭证仍在 OpenAI flow 配)
3. 不需要来源选择器和账户密码 row

实现:
- flows/openai-reauth/index.js: services 改 [] 不再自动加 service-account/email
- sidepanel.html: textarea 替换为 input[type=file] + 校验按钮删除(选文件即触发)
- sidepanel.js:
  * inputReauthAccountFile change handler: FileReader 读 → 缓存 lastReauthFileText → 解析
  * lastReauthFileText 缓存供 step 按钮 / AUTO_RUN 复用
  * applyOpenAiReauthRowVisibility: 切到 reauth 时隐藏 row-source-selector 和 row-custom-password
    (用 dataset.reauthHidden 标记,切回其他 flow 时恢复)

reauth 测试 52/52 通过
- 复用注册流程 step9 的多策略点击编排(content / debugger)+ 多轮重试 + 刷新降级
- capture-reauth-callback 新增 drivePrimaryContinueClick 并行任务,与 localhost 回调监听并行运行
- 守护 consentClickEnabled:依赖未注入时优雅降级为原"干等回调"行为
- background.js 装配处注入 16 个 deps(10 函数 + 4 常量 + 2 工具),全部复用 step9 已有实现
- 修复 codex_cli_simplified_flow=true 并不会让 OAuth 跳过同意页的假设破产

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- REAUTH_HIDDEN_ROW_IDS 扩展隐藏 row-mail-provider / row-email-generator / row-auto-run-controls
- 新增 syncReauthMailProviderToGlobal:reauth 邮箱来源选择同步到全局 mailProvider,复用 provider 配置卡片可见性
- 拆分 persist 参数:用户主动 change 时 persist=true 写 storage,UI 刷新调用时 persist=false 仅同步显示
- 修复 applyOpenAiReauthRowVisibility → syncReauthMailProviderToGlobal → saveSettings → STATE_CHANGED 广播回环导致的 SAVE_SETTING 死循环刷屏

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- fetch-reauth-code 包装 pollFlowVerificationCode 为 4 轮 poll+resend 循环(最多 3 次主动重发)
- 失败后通过 sendToContentScriptResilient 复用 openai-auth 的 RESEND_VERIFICATION_CODE handler
- 2925 单轮 poll attempts 从默认 15 缩到 6(90s),让 resend 在 ~1.5 分钟即可介入
- 错误处理:stop 信号秒抛、resend 失败不中断主轮询、用完所有轮 throw 最终 error
- background.js 注入 sleepWithStop 让 cooldown 等待可被 stop 中断
- 新增 tests/openai-reauth-fetch-code.test.js 11 个用例,覆盖 skip/首轮成功/二轮 resend/全失败/stop/payload overrides/FILL_CODE 错误

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 新建 flows/openai-reauth/background/batch-runner.js,按队列依次跑每个账号 step1→step4
- 单账号失败默认自动跳过(skipOnFailure=true),累计到 failed 数组不中断
- 维护 reauthBatchRunning / reauthBatchProgress / reauthBatchResult 三个 state 字段
- mergeBatchResultsIntoFile 支持 sub2api 整文件 / accounts 数组 / 顶层数组 / 单账号 4 种 schema
- 失败账号保留原 entry,成功账号 merge 新 token 字段(保留 priority / concurrency 等 metadata)
- background.js 加载 batch-runner.js,通过 getReauthBatchRunner getter 解决与 messageRouter 的依赖时序
- message-router 新增 START_REAUTH_BATCH case,fire-and-forget 后台执行,进度通过 setState 广播
- flows/openai-reauth/index.js settingsGroups.reauth-input.rowIds 增加 row-reauth-batch-progress
- manifest.json 新增 downloads 权限,为 sidepanel 一键下载整文件 JSON 做准备
- STOP 复用现有 STOP_FLOW + throwIfStopped 路径,batch-runner 内部 stop 信号秒抛 + 写 aborted=true

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- sidepanel.html 加批量模式 toggle(>=2 账号时可见),开启后隐藏单账号下拉
- 新增 row-reauth-batch-progress 行,实时显示「[当前/总数] 邮箱 状态」与 summary
- 加「开始批量授权」「停止」「下载文件」三个按钮,按批量模式动态显示
- sidepanel.js 新增 applyReauthBatchUiVisibility / applyReauthBatchProgress / applyReauthBatchResult
- buildReauthBatchAccounts 把 pendingReauthAccounts 注入 mailProvider 后构造 batch 队列
- handleReauthBatchStart 发送 START_REAUTH_BATCH 消息(fire-and-forget)
- handleReauthBatchStop 复用 STOP_FLOW 消息
- handleReauthBatchDownload 优先用 chrome.downloads.download 弹保存对话框,失败 fallback 到剪贴板
- 文件名格式:sub2api-reauth-YYYYMMDD-HHmmss.json
- GET_STATE / NODE_STATUS_CHANGED 路径都调用 applyReauthBatchProgress / Result,保持 UI 与 storage 同步

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…nelModeUI 测试

- tests/openai-reauth-batch-runner.test.js 覆盖 extractAccountEmail / mergeBatchResultsIntoFile 纯函数
- 覆盖 executeReauthBatch 主流程:顺序 3 账号全成功 / 中间失败 skip 继续 / skipOnFailure=false 中断
- 覆盖 stop 信号立即终止并写 aborted=true & stopReason=user_stop
- 覆盖 progress currentStatus 流转、runSingleAccount 注入 mailProvider、每账号 reset state
- 覆盖 interAccountDelayMs > 0 时账号间 sleep(最后一个不 sleep)
- 修复 sidepanel-flow-source-registry.test.js 在 sandbox 中漏 mock applyOpenAiReauthRowVisibility
- 同步 assert 链增加 reauth-visibility 调用顺序检查

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 拆分 reauth-input 节点 rowIds,引入 mode-picker / provider-picker / batch-actions 三个新行
- 重组 reauth UI 容器:账号文件 / 处理模式 / 邮箱来源 / 批量操作 / 进度 / 结果分区
- 新增 reauth 专属样式:status-chip / mode-zone / progress-bar / result-summary

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- background.setState 写入 chrome.storage.session 后广播 STATE_PATCH,让 sidepanel 即时同步增量
- sidepanel onMessage 新增 STATE_PATCH 处理,按字段 dispatch 到 applyReauthBatchProgress / applyReauthBatchResult
- 修复 batch 完成时下载按钮卡 disabled / summary 卡「等待…」/ 进度 badge 卡「处理中」的 race condition
- 移除 reauth 流程 UI 残留 emoji(✅ ❌ ⚠)
- 同步重组后的 reauth DOM 引用与批量模式/邮箱来源/批量操作 handler

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- S1: MAIL_PROVIDER_OPTIONS 双端单一数据源,sidepanel 动态渲染
- S2: fetch-reauth-code totalRounds 计算逻辑注释
- S3: openai-reauth 版本公告草稿
- S5: .gitattributes 强制 LF 行尾
…H 日志 / consentClick 日志 / CLIENT_ID 可注入

- batch-runner 改用 validator 统一 extractAccountEmail,保留 fallback
- STATE_PATCH 广播 catch 区分「无接收方」与真实错误
- capture-reauth-callback 在 deps 校验处输出 consentClick 能力日志
- oauth-client 的 buildAuthorizeUrl / exchangeAuthorizationCode / buildUpdatedAccount 接受可选 clientId 参数
检测调用从 waitForStep8Ready(30s超时) 之后移到之前,新增 tab 激活后立即预检。
Copilot AI review requested due to automatic review settings May 29, 2026 07:29
@ranger1112 ranger1112 changed the title feat(openai-reauth): 新增 OpenAI OAuth 重新授权独立流程Feat/OpenAI reauth flow feat(openai-reauth): 新增 OpenAI OAuth 重新授权独立流程 May 29, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR introduces a new "OpenAI 重新授权" (reauth) flow that allows users to re-authorize OpenAI accounts whose refresh_token has been revoked. The flow runs a full OAuth PKCE handshake, captures the localhost callback, and updates account credentials. It supports single-account and batch modes with downloadable JSON output.

Changes:

  • New flows/openai-reauth/ module with 4 steps (prepare → submit email → fetch code → capture callback), OAuth client, cookie cleanup, batch runner, mail rules, and account validator
  • Sidepanel UI for file upload, mode/provider selection, progress display, and result download; new STATE_PATCH broadcast for incremental UI updates
  • Manifest gains downloads permission and registers the new flow scripts; message router adds START_REAUTH_BATCH handler

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
flows/openai-reauth/index.js Flow definition (id, sources, nodes, settings groups)
flows/openai-reauth/workflow.js Static step variant definitions
flows/openai-reauth/reauth-account-validator.js JSON parsing + email/provider validation
flows/openai-reauth/mail-rules.js Mail polling rule builder for reauth code
flows/openai-reauth/background/oauth-client.js PKCE / authorize URL / token exchange / account update
flows/openai-reauth/background/cookie-cleanup.js Clear OpenAI cookies across stores
flows/openai-reauth/background/batch-runner.js Sequentially run reauth for multiple accounts, merge into file
flows/openai-reauth/background/steps/*.js Four node executors driving the OAuth flow
flows/openai/content/openai-auth.js New DETECT_ACCOUNT_BANNED content-script command
flows/index.js Register openai-reauth flow in registry
background.js Import reauth scripts, wire executors, broadcast STATE_PATCH from setState
background/message-router.js New START_REAUTH_BATCH handler; pass reauthInputAccount in EXECUTE_NODE/AUTO_RUN
sidepanel/sidepanel.{html,css,js} New reauth UI rows, progress bar, result/download controls and handlers
manifest.json Add downloads permission, register reauth scripts in content_scripts
tests/*.test.js Unit tests for validator, oauth-client, mail-rules, batch-runner, fetch-code, cookie-cleanup, capture-callback, and updated sidepanel-flow registry test
.gitignore / .gitattributes Ignore .omc/ and /tmp/; enforce LF line endings

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +103 to +108
// 建设性日志:标记 consent 主动点击能力是否就绪,方便排查步骤 4 行为差异。
if (!consentClickEnabled) {
logStep('OAuth 同意页主动点击能力未注入(部分 step9 辅助函数缺失),步骤 4 将仅依赖 localhost 回调监听。', 'warn');
}

function logStep(message, level = 'info') {
Comment on lines +26 to +29
function extractAccountEmail(account = {}) {
// 优先走 validator 的统一实现,保持双端 email 提取逻辑一致。
const validator = (typeof self !== 'undefined' ? self : globalThis)
.MultiPageOpenAiReauthAccountValidator;
Comment on lines +275 to +280
onBeforeNavigate = handleNavigation;
onCommitted = handleNavigation;
onTabUpdated = handleTabUpdated;
chromeApi.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate);
chromeApi.webNavigation.onCommitted.addListener(onCommitted);
chromeApi.tabs.onUpdated.addListener(onTabUpdated);
Comment thread sidepanel/sidepanel.js
async function handleReauthBatchStop() {
try {
await chrome.runtime.sendMessage({ type: 'STOP_FLOW', source: 'sidepanel', payload: {} });
setReauthJsonStatus('已请求停止批量授权...', 'warn');
Comment on lines +278 to +280
} catch (error) {
const stopped = isLikelyStopError(error);
await setState({
Comment on lines +85 to +87
if (!chromeApi?.webNavigation || !chromeApi?.tabs) {
throw new Error('capture-reauth-callback executor 需要 chrome.webNavigation / chrome.tabs。');
}
Comment thread sidepanel/sidepanel.js Outdated
const url = URL.createObjectURL(blob);
await chrome.downloads.download({ url, filename, saveAs: true });
// 让浏览器自己处理 url 生命周期;保险起见 30 秒后 revoke
setTimeout(() => { try { URL.revokeObjectURL(url); } catch {} }, 30000);
Comment on lines +129 to +131
const decoded = typeof atob === 'function'
? atob(padded)
: Buffer.from(padded, 'base64').toString('binary');
Comment thread manifest.json
"debugger",
"browsingData",
"cookies",
"downloads",
Comment on lines +50 to +52
const stores = chromeApi.cookies.getAllCookieStores
? await chromeApi.cookies.getAllCookieStores()
: [{ id: undefined }];
- 接入批量授权账号邮箱提取的依赖注入,避免运行器隐式依赖全局校验器
- 修复批量终止状态写入、构造期日志、下载链接释放和状态样式清理的边界问题
- 补齐批量运行器与回调捕获执行器的防回归测试覆盖
- 识别 OAuth 重新授权后的手机验证页与手机号补充页
- 将手机验证要求转换为账号级 fatal 错误,批量流程可跳过当前账号继续执行
- 补充步骤 4 手机验证场景测试,覆盖监听器清理与不误完成节点
- 在步骤4监听回调前后增加认证页即时预检
- 通过标签页快照、登录状态消息和页面文本识别手机验证阻断
- 补充启动预检测试,确保不会等待 OAuth ready 超时才跳过账号
@QLHazyCoder
Copy link
Copy Markdown
Owner

QLHazyCoder commented May 29, 2026

这个 PR 现在先不要继续合并,请先同步最新 dev 后再更新 PR。

我这边刚重新拉取并核对了一次:

  • 当前 PR 目标分支还是 master,按仓库流程需要改为 dev
  • PR head 是 237d28d,最新 origin/dev3c0bc41
  • origin/dev 不是当前 PR 分支的祖先,PR 分支还没有吸收最新 dev
  • 当前 PR 相对最新 dev 还缺 29 个提交,最近包括 3c0bc41 fix: enhance OAuth session handling and add tests for state resets

请在你的功能分支上先执行一次同步,比如优先 rebase 到最新 origin/dev,如果这个分支多人共用再改用 merge:

git fetch origin
git rebase origin/dev
# 如有冲突,解决后继续 rebase,并重新跑相关测试
git push --force-with-lease origin feat/openai-reauth-flow

如果不用 rebase,也可以 git merge origin/dev 后正常 push。处理完后请把 PR 目标分支改成 dev,再重新更新/提交这个 PR。同步后我再继续看功能 diff 和测试结果。

@ranger1112
Copy link
Copy Markdown
Author

Closing this PR because it was based on master.
A new PR has been opened against dev as requested: #295

@ranger1112 ranger1112 closed this May 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants