codex-oauth-automation-extension

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Codex OAuth Automation Extension

Codex OAuth自动化扩展

Skill by ara.so — Daily 2026 Skills collection.
A Chrome extension that automates the full OpenAI OAuth registration/login flow including email verification, CPA callback handling, and multi-round batch execution. Supports DuckDuckGo, QQ Mail, 163 Mail, and Inbucket as verification code sources.
ara.so提供的技能 —— 2026每日技能合集。
这是一款Chrome扩展,可自动化完整的OpenAI OAuth注册/登录流程,包括邮箱验证、CPA回调处理和多轮批量执行。支持DuckDuckGo、QQ邮箱、163邮箱和Inbucket作为验证码来源。

Installation

安装

  1. Clone the repository:
bash
git clone https://github.com/QLHazyCoder/codex-oauth-automation-extension.git
cd codex-oauth-automation-extension
  1. Load in Chrome:
    • Navigate to
      chrome://extensions/
    • Enable Developer mode (top right toggle)
    • Click Load unpacked
    • Select the project directory
  2. Open the side panel from the Chrome toolbar to configure and run.
  1. 克隆仓库:
bash
git clone https://github.com/QLHazyCoder/codex-oauth-automation-extension.git
cd codex-oauth-automation-extension
  1. 在Chrome中加载:
    • 导航至
      chrome://extensions/
    • 启用开发者模式(右上角开关)
    • 点击加载已解压的扩展程序
    • 选择项目目录
  2. 从Chrome工具栏打开侧边面板进行配置和运行。

Project Structure

项目结构

background.js              # Main orchestrator: steps 1–9, tab management, state
manifest.json              # Extension manifest
data/names.js              # Random name/birthday data
content/utils.js           # Shared helpers: waitForElement, click, stop control
content/vps-panel.js       # CPA panel: Step 1 / Step 9
content/signup-page.js     # OpenAI signup/login: Steps 2/3/5/6/8
content/duck-mail.js       # DuckDuckGo @duck.com address generation
content/qq-mail.js         # QQ Mail OTP polling
content/mail-163.js        # 163 Mail OTP polling
content/inbucket-mail.js   # Inbucket mailbox OTP polling
sidepanel/                 # Sidebar UI (HTML/CSS/JS)
background.js              # 主协调器:步骤1–9、标签页管理、状态
manifest.json              # 扩展清单
data/names.js              # 随机姓名/生日数据
content/utils.js           # 共享工具函数:waitForElement、click、停止控制
content/vps-panel.js       # CPA面板:步骤1 / 步骤9
content/signup-page.js     # OpenAI注册/登录:步骤2/3/5/6/8
content/duck-mail.js       # DuckDuckGo @duck.com地址生成
content/qq-mail.js         # QQ邮箱OTP轮询
content/mail-163.js        # 163邮箱OTP轮询
content/inbucket-mail.js   # Inbucket邮箱OTP轮询
sidepanel/                 # 侧边栏UI(HTML/CSS/JS)

Configuration (Side Panel Fields)

配置(侧边面板字段)

FieldDescriptionExample
CPA
Your OAuth management panel URL
https://your-host/management.html#/oauth
Mail
Verification code source
163 Mail
,
QQ Mail
, or
Inbucket
Email
Registration email (or click Auto for @duck.com)
user@duck.com
Password
Custom password; leave blank to auto-generate
MyPass123!
Inbucket
Inbucket host (only when Mail=Inbucket)
your-inbucket-host
Mailbox
Inbucket mailbox name (only when Mail=Inbucket)
tmp-mailbox
字段说明示例
CPA
你的OAuth管理面板URL
https://your-host/management.html#/oauth
Mail
验证码来源
163 Mail
QQ Mail
Inbucket
Email
注册邮箱(或点击Auto生成@duck.com邮箱)
user@duck.com
Password
自定义密码;留空则自动生成
MyPass123!
Inbucket
Inbucket主机地址(仅当Mail=Inbucket时需填写)
your-inbucket-host
Mailbox
Inbucket邮箱名称(仅当Mail=Inbucket时需填写)
tmp-mailbox

Storage Behavior

存储行为

  • chrome.storage.session
    — runtime state (steps, OAuth link, email, password, callback URL, tab info). Cleared when browser closes.
  • chrome.storage.local
    — persistent config (CPA URL, password, mail service, Inbucket settings). Survives browser restarts.
  • chrome.storage.session
    —— 运行时状态(步骤、OAuth链接、邮箱、密码、回调URL、标签页信息)。浏览器关闭时会清除。
  • chrome.storage.local
    —— 持久化配置(CPA URL、密码、邮件服务、Inbucket设置)。浏览器重启后仍保留。

The 9-Step Workflow

9步工作流程

Step 1: Get OAuth Link

步骤1:获取OAuth链接

Opens CPA panel → finds
Codex OAuth
card → clicks login → extracts authorization URL → saves to
OAuth
field.
打开CPA面板 → 找到
Codex OAuth
卡片 → 点击登录 → 提取授权URL → 保存到
OAuth
字段。

Step 2: Open Signup

步骤2:打开注册页面

Opens the OAuth authorization link → locates and clicks
Sign up / Register / 创建账户
button.
打开OAuth授权链接 → 定位并点击
Sign up / Register / 创建账户
按钮。

Step 3: Fill Email / Password

步骤3:填写邮箱/密码

Fills registration form with email and password. Auto-generates strong password if field is blank. Actual password used is written back to the sidebar.
用邮箱和密码填写注册表单。若密码字段留空,会自动生成强密码。实际使用的密码会回写到侧边栏。

Step 4: Get Signup Code

步骤4:获取注册验证码

Polls the configured mailbox for a 6-digit OTP. Handles
Operation timed out
errors by auto-clicking retry.
Email matching rules:
  • Sender contains:
    openai
    ,
    noreply
    ,
    verify
    ,
    auth
    ,
    duckduckgo
    ,
    forward
  • Subject contains:
    verify
    ,
    verification
    ,
    code
    ,
    验证
    ,
    confirm
轮询配置的邮箱,获取6位OTP。遇到
Operation timed out
错误时会自动点击重试。
邮箱匹配规则:
  • 发件人包含:
    openai
    noreply
    verify
    auth
    duckduckgo
    forward
  • 主题包含:
    verify
    verification
    code
    验证
    confirm

Step 5: Fill Name / Birthday

步骤5:填写姓名/生日

Generates random name and birthday. Handles two page variants:
  • Birthday mode: fills year/month/day
  • Age mode: fills
    input[name='age']
    directly
生成随机姓名和生日。支持两种页面变体:
  • 生日模式:填写年/月/日
  • 年龄模式:直接填写
    input[name='age']

Step 6: Login via OAuth

步骤6:通过OAuth登录

Re-fetches latest CPA OAuth link, then logs in with the newly registered account.
重新获取最新的CPA OAuth链接,然后使用新注册的账户登录。

Step 7: Get Login Code

步骤7:获取登录验证码

Same as Step 4 but with login-specific keyword matching.
与步骤4相同,但使用登录专属的关键词匹配规则。

Step 8: Manual OAuth Confirm (Auto-attempts)

步骤8:手动OAuth确认(自动尝试)

  • Locates the "Continue/Authorize" button on the OAuth consent page
  • Uses Chrome
    debugger
    API to dispatch input events for the click
  • Monitors
    chrome.webNavigation.onBeforeNavigate
    for localhost callback
  • Only accepts:
    http(s)://localhost:<port>/auth/callback?code=...&state=...
  • Timeout: 120 seconds
  • 定位OAuth授权页面上的“Continue/Authorize”按钮
  • 使用Chrome
    debugger
    API触发点击的输入事件
  • 监听
    chrome.webNavigation.onBeforeNavigate
    以捕获localhost回调
  • 仅接受:
    http(s)://localhost:<port>/auth/callback?code=...&state=...
  • 超时时间:120秒

Step 9: CPA Verify

步骤9:CPA验证

  • Validates callback URL has both
    code
    and
    state
    params
  • Submits callback to CPA panel
  • Waits for exact
    认证成功!
    status badge
  • Closes residual
    http://localhost:1455/auth*
    tabs
  • 验证回调URL同时包含
    code
    state
    参数
  • 将回调提交至CPA面板
  • 等待出现精确的
    认证成功!
    状态标识
  • 关闭残留的
    http://localhost:1455/auth*
    标签页

Auto Mode

自动模式

Click Auto in the sidebar to run all 9 steps sequentially for N rounds (set by the number input).
Auto flow:
  Step 1 → Step 2 → [Duck email auto-fetch, retry up to 5x]
    ↓ (if Duck fails) → Pause, wait for manual email input → Continue
  Step 3 → Step 4 → Step 5 → Step 6 → Step 7 → Step 8 → Step 9
  → Repeat for N rounds
点击侧边栏的Auto按钮,即可按顺序运行全部9步流程N轮(轮数由数字输入框设置)。
自动流程:
  步骤1 → 步骤2 → [自动获取Duck邮箱,最多重试5次]
    ↓(若Duck邮箱获取失败)→ 暂停,等待手动输入邮箱 → 继续
  步骤3 → 步骤4 → 步骤5 → 步骤6 → 步骤7 → 步骤8 → 步骤9
  → 重复N轮

Resuming After Pause

暂停后恢复

When Auto is paused and you reopen the sidebar, two options appear:
  • 重新开始 — Reset progress, start new round from Step 1
  • 继续当前 — Treat completed/skipped steps as done, resume from first unhandled step
当自动流程暂停并重新打开侧边栏时,会出现两个选项:
  • 重新开始 —— 重置进度,从步骤1开始新一轮
  • 继续当前 —— 将已完成/跳过的步骤视为已处理,从第一个未处理的步骤恢复

Key Code Patterns

核心代码模式

Waiting for Elements (content/utils.js pattern)

等待元素(content/utils.js模式)

javascript
// Wait for a DOM element with stop-signal support
async function waitForElement(selector, timeout = 30000) {
  const start = Date.now();
  while (Date.now() - start < timeout) {
    // Check stop signal from background
    const { stopFlow } = await chrome.storage.session.get('stopFlow');
    if (stopFlow) throw new Error('STOPPED');

    const el = document.querySelector(selector);
    if (el) return el;
    await new Promise(r => setTimeout(r, 500));
  }
  throw new Error(`Timeout waiting for: ${selector}`);
}
javascript
// 等待DOM元素,支持停止信号
async function waitForElement(selector, timeout = 30000) {
  const start = Date.now();
  while (Date.now() - start < timeout) {
    // 检查来自background的停止信号
    const { stopFlow } = await chrome.storage.session.get('stopFlow');
    if (stopFlow) throw new Error('STOPPED');

    const el = document.querySelector(selector);
    if (el) return el;
    await new Promise(r => setTimeout(r, 500));
  }
  throw new Error(`Timeout waiting for: ${selector}`);
}

Reading/Writing Session State (background.js pattern)

读写会话状态(background.js模式)

javascript
// Save OAuth link to session
await chrome.storage.session.set({ oauthLink: extractedUrl });

// Read current email and password
const { currentEmail, currentPassword } = await chrome.storage.session.get([
  'currentEmail',
  'currentPassword'
]);

// Update step status
await chrome.storage.session.set({
  stepStatus: { ...existingStatus, step3: 'done' }
});
javascript
// 将OAuth链接保存到会话
await chrome.storage.session.set({ oauthLink: extractedUrl });

// 读取当前邮箱和密码
const { currentEmail, currentPassword } = await chrome.storage.session.get([
  'currentEmail',
  'currentPassword'
]);

// 更新步骤状态
await chrome.storage.session.set({
  stepStatus: { ...existingStatus, step3: 'done' }
});

Sending Messages to Content Scripts

向内容脚本发送消息

javascript
// background.js → content script
const [tab] = await chrome.tabs.query({ url: '*://chat.openai.com/*' });
const result = await chrome.tabs.sendMessage(tab.id, {
  action: 'FILL_EMAIL',
  email: 'user@duck.com',
  password: 'GeneratedPass1!'
});
javascript
// background.js → 内容脚本
const [tab] = await chrome.tabs.query({ url: '*://chat.openai.com/*' });
const result = await chrome.tabs.sendMessage(tab.id, {
  action: 'FILL_EMAIL',
  email: 'user@duck.com',
  password: 'GeneratedPass1!'
});

Inbucket Mailbox Polling (content/inbucket-mail.js pattern)

Inbucket邮箱轮询(content/inbucket-mail.js模式)

javascript
// Only targets unread messages
const unseenEntries = document.querySelectorAll('.message-list-entry.unseen');
// From 2nd poll onwards, click the refresh button
if (pollCount > 1) {
  const refreshBtn = document.querySelector('[data-action="refresh"]');
  if (refreshBtn) refreshBtn.click();
  await sleep(1000);
}
// After reading, delete the email to avoid re-matching
javascript
// 仅针对未读消息
const unseenEntries = document.querySelectorAll('.message-list-entry.unseen');
// 从第2次轮询开始,点击刷新按钮
if (pollCount > 1) {
  const refreshBtn = document.querySelector('[data-action="refresh"]');
  if (refreshBtn) refreshBtn.click();
  await sleep(1000);
}
// 读取后删除邮件,避免重复匹配

Chrome Debugger Click (content/signup-page.js pattern)

Chrome调试器点击(content/signup-page.js模式)

javascript
// Attach debugger to tab for synthetic input events
await chrome.debugger.attach({ tabId }, '1.3');
const { x, y } = buttonBounds;
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
  type: 'mousePressed', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
  type: 'mouseReleased', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.detach({ tabId });
javascript
// 附加调试器到标签页以生成合成输入事件
await chrome.debugger.attach({ tabId }, '1.3');
const { x, y } = buttonBounds;
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
  type: 'mousePressed', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.sendCommand({ tabId }, 'Input.dispatchMouseEvent', {
  type: 'mouseReleased', x, y, button: 'left', clickCount: 1
});
await chrome.debugger.detach({ tabId });

OAuth Callback Listener (background.js pattern)

OAuth回调监听器(background.js模式)

javascript
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
  // Only main frame, only the auth tab
  if (details.frameId !== 0) return;
  if (details.tabId !== authTabId) return;

  const url = details.url;
  // Strict: must be localhost /auth/callback with code + state
  if (/^https?:\/\/localhost:\d+\/auth\/callback\?/.test(url)) {
    const parsed = new URL(url);
    if (parsed.searchParams.get('code') && parsed.searchParams.get('state')) {
      await chrome.storage.session.set({ callbackUrl: url });
    }
  }
});
javascript
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
  // 仅主框架、仅认证标签页
  if (details.frameId !== 0) return;
  if (details.tabId !== authTabId) return;

  const url = details.url;
  // 严格匹配:必须是localhost /auth/callback且包含code + state
  if (/^https?:\/\/localhost:\d+\/auth\/callback\?/.test(url)) {
    const parsed = new URL(url);
    if (parsed.searchParams.get('code') && parsed.searchParams.get('state')) {
      await chrome.storage.session.set({ callbackUrl: url });
    }
  }
});

Stop Signal Broadcasting

停止信号广播

javascript
// From sidebar: send stop
await chrome.runtime.sendMessage({ action: 'STOP_FLOW' });

// In background.js: set flag and broadcast to all content scripts
await chrome.storage.session.set({ stopFlow: true });
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
  chrome.tabs.sendMessage(tab.id, { action: 'STOP_FLOW' }).catch(() => {});
}
javascript
// 从侧边栏发送停止指令
await chrome.runtime.sendMessage({ action: 'STOP_FLOW' });

// 在background.js中:设置标志并广播到所有内容脚本
await chrome.storage.session.set({ stopFlow: true });
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
  chrome.tabs.sendMessage(tab.id, { action: 'STOP_FLOW' }).catch(() => {});
}

DuckDuckGo Email Auto-Fetch

DuckDuckGo邮箱自动获取

javascript
// Triggered by sidebar "Auto" button next to Email field
// content/duck-mail.js opens:
// https://duckduckgo.com/email/settings/autofill
// Looks for existing private address or generates new one
const generateBtn = document.querySelector('[data-testid="generate-address"]');
if (generateBtn) generateBtn.click();
await waitForElement('.address-display');
const newAddress = document.querySelector('.address-display').textContent.trim();
javascript
// 由邮箱字段旁的侧边栏"Auto"按钮触发
// content/duck-mail.js打开:
// https://duckduckgo.com/email/settings/autofill
// 查找现有私密地址或生成新地址
const generateBtn = document.querySelector('[data-testid="generate-address"]');
if (generateBtn) generateBtn.click();
await waitForElement('.address-display');
const newAddress = document.querySelector('.address-display').textContent.trim();

Persistent Config (chrome.storage.local)

持久化配置(chrome.storage.local)

javascript
// Save config
await chrome.storage.local.set({
  cpaUrl: 'https://your-host/management.html#/oauth',
  mailService: 'Inbucket',        // '163 Mail' | 'QQ Mail' | 'Inbucket'
  inbucketHost: 'your-inbucket-host',
  inbucketMailbox: 'tmp-mailbox',
  customPassword: '',              // empty = auto-generate
});

// Load config
const config = await chrome.storage.local.get([
  'cpaUrl', 'mailService', 'inbucketHost', 'inbucketMailbox', 'customPassword'
]);
javascript
// 保存配置
await chrome.storage.local.set({
  cpaUrl: 'https://your-host/management.html#/oauth',
  mailService: 'Inbucket',        // '163 Mail' | 'QQ Mail' | 'Inbucket'
  inbucketHost: 'your-inbucket-host',
  inbucketMailbox: 'tmp-mailbox',
  customPassword: '',              // 空值 = 自动生成
});

// 加载配置
const config = await chrome.storage.local.get([
  'cpaUrl', 'mailService', 'inbucketHost', 'inbucketMailbox', 'customPassword'
]);

Troubleshooting

故障排除

Step 8 timeout (120s exceeded)

步骤8超时(超过120秒)

  • The OAuth consent page structure may have changed
  • Manually click the "Continue" button and observe what URL the redirect hits
  • Check the button selector in
    content/signup-page.js
  • OAuth授权页面结构可能已变更
  • 手动点击“Continue”按钮,观察重定向到的URL
  • 检查
    content/signup-page.js
    中的按钮选择器

Step 4/7: OTP never arrives

步骤4/7:OTP始终未收到

  • Verify the mail service tab is open and logged in before running
  • For Inbucket: confirm
    https://<host>/m/<mailbox>/
    is accessible
  • Check sender/subject filters — OpenAI sometimes changes sender addresses
  • For QQ/163: ensure the webmail tab is the correct account
  • 运行前确认邮件服务标签页已打开并登录
  • 对于Inbucket:确认
    https://<host>/m/<mailbox>/
    可访问
  • 检查发件人/主题过滤器 —— OpenAI有时会更改发件人地址
  • 对于QQ/163邮箱:确保网页邮箱标签页对应的是正确账户

Duck email auto-fetch fails (retries 5x then pauses)

Duck邮箱自动获取失败(重试5次后暂停)

  • DuckDuckGo extension must be installed and logged in
  • The autofill settings page URL may have changed
  • Fall back to manual email entry in the sidebar
  • 必须安装并登录DuckDuckGo扩展
  • 自动填充设置页面URL可能已变更
  • 回退到侧边栏手动输入邮箱

CPA panel not detected (Step 1/9)

CPA面板未被检测到(步骤1/9)

  • Confirm your CPA URL matches
    management.html#/oauth
    path structure
  • The
    content/vps-panel.js
    selectors are hardcoded to a specific panel layout
  • Try running Step 1 manually to see console errors
  • 确认你的CPA URL匹配
    management.html#/oauth
    路径结构
  • content/vps-panel.js
    中的选择器是硬编码到特定面板布局的
  • 尝试手动运行步骤1,查看控制台错误

"Operation timed out" on signup (Step 4)

注册时出现"Operation timed out"(步骤4)

  • This is handled automatically — the script clicks the retry button and re-submits
  • If it loops, the OpenAI signup endpoint may be rate-limiting your IP
  • 此情况会自动处理 —— 脚本会点击重试按钮并重新提交
  • 如果循环出现,OpenAI注册端点可能对你的IP进行了限流

Tab cleanup issues

标签页清理问题

  • Old localhost tabs accumulate: Step 9 only cleans
    http://localhost:1455/auth*
  • If your CPA uses a different port, update the cleanup filter in
    background.js
  • 旧的localhost标签页堆积:步骤9仅清理
    http://localhost:1455/auth*
  • 如果你的CPA使用不同端口,请更新
    background.js
    中的清理过滤器

Recommended Workflow

推荐工作流程

1. Configure sidebar (CPA URL, Mail service, credentials)
2. Run Step 1 manually → verify OAuth link appears
3. Run Steps 2-4 manually → confirm email + OTP flow works
4. If successful, enable Auto with N=5 for a test batch
5. Scale up rounds once flow is stable
Note: Always test single-step flow before enabling Auto. The most fragile steps are Step 8 (OAuth consent click) and Step 4/7 (OTP timing). Use Inbucket for most reliable OTP delivery in automated runs.
1. 配置侧边栏(CPA URL、邮件服务、凭据)
2. 手动运行步骤1 → 验证OAuth链接已显示
3. 手动运行步骤2-4 → 确认邮箱+OTP流程正常
4. 若成功,启用自动模式并设置N=5进行测试批量运行
5. 流程稳定后再增加轮数
注意: 启用自动模式前务必测试单步流程。最不稳定的步骤是步骤8(OAuth授权点击)和步骤4/7(OTP时序)。在自动化运行中,使用Inbucket可获得最可靠的OTP投递。