From 7828c196afcfd5ef5fb067c52c8a919d63e1ea7a Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Thu, 16 Apr 2026 22:18:56 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=A7=84=E8=8C=83=E5=8C=96=20OAuth=20?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E9=A1=B5=E9=9D=A2=E7=8A=B6=E6=80=81=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 1 + content/signup-page.js | 56 ++++++------ tests/step6-login-state.test.js | 149 ++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+), 25 deletions(-) create mode 100644 tests/step6-login-state.test.js diff --git a/background.js b/background.js index 0888d57..4cec4a1 100644 --- a/background.js +++ b/background.js @@ -4052,6 +4052,7 @@ function isVerificationMailPollingError(error) { } function getLoginAuthStateLabel(state) { + state = state === 'oauth_consent_page' ? 'unknown' : state; switch (state) { case 'verification_page': return '登录验证码页'; diff --git a/content/signup-page.js b/content/signup-page.js index a9b6772..56ace36 100644 --- a/content/signup-page.js +++ b/content/signup-page.js @@ -1045,13 +1045,6 @@ function inspectLoginAuthState() { }; } - if (oauthConsentPage) { - return { - ...baseState, - state: 'oauth_consent_page', - }; - } - if (passwordInput || switchTrigger) { return { ...baseState, @@ -1097,7 +1090,8 @@ function serializeLoginAuthState(snapshot) { } function getLoginAuthStateLabel(snapshot) { - switch (snapshot?.state) { + const state = snapshot?.state === 'oauth_consent_page' ? 'unknown' : snapshot?.state; + switch (state) { case 'verification_page': return '登录验证码页'; case 'password_page': @@ -1117,11 +1111,11 @@ function getLoginAuthStateLabel(snapshot) { async function waitForKnownLoginAuthState(timeout = 15000) { const start = Date.now(); - let snapshot = inspectLoginAuthState(); + let snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); while (Date.now() - start < timeout) { throwIfStopped(); - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state !== 'unknown') { return snapshot; } @@ -1173,7 +1167,19 @@ function createStep6RecoverableResult(reason, snapshot, options = {}) { }; } +function normalizeStep6Snapshot(snapshot) { + if (snapshot?.state !== 'oauth_consent_page') { + return snapshot; + } + + return { + ...snapshot, + state: 'unknown', + }; +} + function throwForStep6FatalState(snapshot) { + snapshot = normalizeStep6Snapshot(snapshot); switch (snapshot?.state) { case 'oauth_consent_page': throw new Error(`当前页面已进入 OAuth 授权页,未经过登录验证码页,无法完成步骤 6。URL: ${snapshot.url}`); @@ -1476,11 +1482,11 @@ async function fillVerificationCode(step, payload) { async function waitForStep6EmailSubmitTransition(emailSubmittedAt, timeout = 12000) { const start = Date.now(); - let snapshot = inspectLoginAuthState(); + let snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); while (Date.now() - start < timeout) { throwIfStopped(); - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return { @@ -1516,7 +1522,7 @@ async function waitForStep6EmailSubmitTransition(emailSubmittedAt, timeout = 120 await sleep(250); } - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return { action: 'done', @@ -1554,11 +1560,11 @@ async function waitForStep6EmailSubmitTransition(emailSubmittedAt, timeout = 120 async function waitForStep6PasswordSubmitTransition(passwordSubmittedAt, timeout = 10000) { const start = Date.now(); - let snapshot = inspectLoginAuthState(); + let snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); while (Date.now() - start < timeout) { throwIfStopped(); - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return { @@ -1590,7 +1596,7 @@ async function waitForStep6PasswordSubmitTransition(passwordSubmittedAt, timeout await sleep(250); } - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return { action: 'done', @@ -1628,11 +1634,11 @@ async function waitForStep6PasswordSubmitTransition(passwordSubmittedAt, timeout async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeout = 10000) { const start = Date.now(); - let snapshot = inspectLoginAuthState(); + let snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); while (Date.now() - start < timeout) { throwIfStopped(); - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return createStep6SuccessResult(snapshot, { @@ -1658,7 +1664,7 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou await sleep(250); } - snapshot = inspectLoginAuthState(); + snapshot = normalizeStep6Snapshot(inspectLoginAuthState()); if (snapshot.state === 'verification_page') { return createStep6SuccessResult(snapshot, { via: 'switch_to_one_time_code_login', @@ -1685,7 +1691,7 @@ async function waitForStep6SwitchTransition(loginVerificationRequestedAt, timeou async function step6SwitchToOneTimeCodeLogin(snapshot) { const switchTrigger = snapshot?.switchTrigger || findOneTimeCodeLoginTrigger(); if (!switchTrigger || !isActionEnabled(switchTrigger)) { - return createStep6RecoverableResult('missing_one_time_code_trigger', inspectLoginAuthState(), { + return createStep6RecoverableResult('missing_one_time_code_trigger', normalizeStep6Snapshot(inspectLoginAuthState()), { message: '当前登录页没有可用的一次性验证码登录入口。', }); } @@ -1700,7 +1706,7 @@ async function step6SwitchToOneTimeCodeLogin(snapshot) { } async function step6LoginFromPasswordPage(payload, snapshot) { - const currentSnapshot = snapshot || inspectLoginAuthState(); + const currentSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState()); if (currentSnapshot.passwordInput) { if (!payload.password) { @@ -1730,7 +1736,7 @@ async function step6LoginFromPasswordPage(payload, snapshot) { return step6SwitchToOneTimeCodeLogin(transition.snapshot); } - return createStep6RecoverableResult('password_submit_unknown', inspectLoginAuthState(), { + return createStep6RecoverableResult('password_submit_unknown', normalizeStep6Snapshot(inspectLoginAuthState()), { message: '提交密码后未得到可用的下一步状态。', }); } @@ -1745,7 +1751,7 @@ async function step6LoginFromPasswordPage(payload, snapshot) { } async function step6LoginFromEmailPage(payload, snapshot) { - const currentSnapshot = snapshot || inspectLoginAuthState(); + const currentSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState()); const emailInput = currentSnapshot.emailInput || getLoginEmailInput(); if (!emailInput) { throw new Error('在登录页未找到邮箱输入框。URL: ' + location.href); @@ -1777,7 +1783,7 @@ async function step6LoginFromEmailPage(payload, snapshot) { return step6LoginFromPasswordPage(payload, transition.snapshot); } - return createStep6RecoverableResult('email_submit_unknown', inspectLoginAuthState(), { + return createStep6RecoverableResult('email_submit_unknown', normalizeStep6Snapshot(inspectLoginAuthState()), { message: '提交邮箱后未得到可用的下一步状态。', }); } @@ -1788,7 +1794,7 @@ async function step6_login(payload) { log(`步骤 6:正在使用 ${email} 登录...`); - const snapshot = await waitForKnownLoginAuthState(15000); + const snapshot = normalizeStep6Snapshot(await waitForKnownLoginAuthState(15000)); if (snapshot.state === 'verification_page') { log('步骤 6:登录验证码页面已就绪。', 'ok'); diff --git a/tests/step6-login-state.test.js b/tests/step6-login-state.test.js new file mode 100644 index 0000000..119ca13 --- /dev/null +++ b/tests/step6-login-state.test.js @@ -0,0 +1,149 @@ +const assert = require('assert'); +const fs = require('fs'); + +const source = fs.readFileSync('content/signup-page.js', 'utf8'); + +function extractFunction(name) { + const markers = [`async function ${name}(`, `function ${name}(`]; + const start = markers + .map((marker) => source.indexOf(marker)) + .find((index) => index >= 0); + if (start < 0) { + throw new Error(`missing function ${name}`); + } + + let parenDepth = 0; + let signatureEnded = false; + let braceStart = -1; + for (let i = start; i < source.length; i += 1) { + const ch = source[i]; + if (ch === '(') { + parenDepth += 1; + } else if (ch === ')') { + parenDepth -= 1; + if (parenDepth === 0) { + signatureEnded = true; + } + } else if (ch === '{' && signatureEnded) { + braceStart = i; + break; + } + } + if (braceStart < 0) { + throw new Error(`missing body for function ${name}`); + } + + let depth = 0; + let end = braceStart; + for (; end < source.length; end += 1) { + const ch = source[end]; + if (ch === '{') depth += 1; + if (ch === '}') { + depth -= 1; + if (depth === 0) { + end += 1; + break; + } + } + } + + return source.slice(start, end); +} + +const bundle = [ + extractFunction('inspectLoginAuthState'), + extractFunction('normalizeStep6Snapshot'), +].join('\n'); + +function createApi(overrides = {}) { + return new Function(` +const location = { + href: ${JSON.stringify(overrides.href || 'https://auth.openai.com/log-in')}, + pathname: ${JSON.stringify(overrides.pathname || '/log-in')}, +}; + +function getLoginTimeoutErrorPageState() { + return ${JSON.stringify(overrides.retryState || null)}; +} + +function getVerificationCodeTarget() { + return ${JSON.stringify(overrides.verificationTarget || null)}; +} + +function getLoginPasswordInput() { + return ${JSON.stringify(overrides.passwordInput || null)}; +} + +function getLoginEmailInput() { + return ${JSON.stringify(overrides.emailInput || null)}; +} + +function findOneTimeCodeLoginTrigger() { + return ${JSON.stringify(overrides.switchTrigger || null)}; +} + +function getLoginSubmitButton() { + return ${JSON.stringify(overrides.submitButton || null)}; +} + +function isVerificationPageStillVisible() { + return ${JSON.stringify(Boolean(overrides.verificationVisible))}; +} + +function isAddPhonePageReady() { + return ${JSON.stringify(Boolean(overrides.addPhonePage))}; +} + +function isStep8Ready() { + return ${JSON.stringify(Boolean(overrides.consentReady))}; +} + +function isOAuthConsentPage() { + return ${JSON.stringify(Boolean(overrides.oauthConsentPage))}; +} + +${bundle} + +return { + inspectLoginAuthState, + normalizeStep6Snapshot, +}; +`)(); +} + +{ + const api = createApi({ + emailInput: { id: 'email' }, + submitButton: { id: 'submit' }, + oauthConsentPage: true, + consentReady: true, + }); + + const snapshot = api.inspectLoginAuthState(); + assert.strictEqual( + snapshot.state, + 'email_page', + '第六步在 /log-in 页应优先识别为邮箱页' + ); +} + +{ + const api = createApi({ + oauthConsentPage: true, + consentReady: true, + }); + + const snapshot = api.normalizeStep6Snapshot({ + state: 'oauth_consent_page', + url: 'https://auth.openai.com/authorize', + }); + + assert.strictEqual(snapshot.state, 'unknown', '第六步应忽略 oauth_consent_page 状态'); +} + +assert.ok( + !extractFunction('inspectLoginAuthState').includes("state: 'oauth_consent_page'"), + 'inspectLoginAuthState 不应再产出 oauth_consent_page 状态' +); + +console.log('step6 login state tests passed');