Fix dynamic auth tail node propagation
This commit is contained in:
@@ -212,7 +212,10 @@
|
||||
{
|
||||
type: 'SUBMIT_ADD_EMAIL',
|
||||
source: 'background',
|
||||
payload: { email: resolvedEmail },
|
||||
payload: {
|
||||
email: resolvedEmail,
|
||||
nodeId: state?.nodeId || activeFetchLoginCodeStepKey || 'fetch-login-code',
|
||||
},
|
||||
},
|
||||
{
|
||||
timeoutMs,
|
||||
|
||||
@@ -307,7 +307,7 @@
|
||||
'signup-page',
|
||||
{
|
||||
type: 'EXECUTE_NODE',
|
||||
nodeId: 'oauth-login',
|
||||
nodeId: state?.nodeId || 'oauth-login',
|
||||
step: 7,
|
||||
source: 'background',
|
||||
payload: {
|
||||
|
||||
@@ -94,15 +94,15 @@
|
||||
}
|
||||
|
||||
const code = normalizeString(parsed.searchParams.get('code'));
|
||||
const state = normalizeString(parsed.searchParams.get('state'));
|
||||
if (!code || !state) {
|
||||
const oauthState = normalizeString(parsed.searchParams.get('state'));
|
||||
if (!code || !oauthState) {
|
||||
throw new Error(`步骤 ${platformVerifyStep} 捕获到的 localhost OAuth 回调地址缺少 code 或 state,请重新执行步骤 ${confirmOauthStep}。`);
|
||||
}
|
||||
|
||||
return {
|
||||
url: parsed.toString(),
|
||||
code,
|
||||
state,
|
||||
state: oauthState,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -503,15 +503,15 @@
|
||||
}
|
||||
|
||||
const code = normalizeString(parsed.searchParams.get('code'));
|
||||
const state = normalizeString(parsed.searchParams.get('state'));
|
||||
if (!code || !state) {
|
||||
const oauthState = normalizeString(parsed.searchParams.get('state'));
|
||||
if (!code || !oauthState) {
|
||||
throw new Error('回调 URL 中缺少 code 或 state。');
|
||||
}
|
||||
|
||||
return {
|
||||
url: parsed.toString(),
|
||||
code,
|
||||
state,
|
||||
state: oauthState,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1117,6 +1117,7 @@
|
||||
}
|
||||
|
||||
await chrome.tabs.update(signupTabId, { active: true });
|
||||
const completionNodeId = await getNodeIdForStep(completionStep);
|
||||
const baseResponseTimeoutMs = await getResponseTimeoutMsForStep(
|
||||
step,
|
||||
step === 8
|
||||
@@ -1134,6 +1135,7 @@
|
||||
source: 'background',
|
||||
payload: {
|
||||
code,
|
||||
...(completionNodeId ? { nodeId: completionNodeId } : {}),
|
||||
...(step === 4 && options.signupProfile ? { signupProfile: options.signupProfile } : {}),
|
||||
},
|
||||
};
|
||||
@@ -1268,6 +1270,7 @@
|
||||
async function resolveVerificationStep(step, state, mail, options = {}) {
|
||||
const completionStep = getCompletionStep(step, options);
|
||||
activeVerificationLogStep = completionStep;
|
||||
const completionNodeId = await getNodeIdForStep(completionStep);
|
||||
const stateKey = getVerificationCodeStateKey(step);
|
||||
const rejectedCodes = new Set();
|
||||
const hotmailPollConfig = mail.provider === HOTMAIL_PROVIDER
|
||||
@@ -1419,7 +1422,6 @@
|
||||
[stateKey]: result.code,
|
||||
});
|
||||
|
||||
const completionNodeId = await getNodeIdForStep(completionStep);
|
||||
if (!completionNodeId) {
|
||||
throw new Error(`步骤 ${completionStep} 未映射到验证码节点。`);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ const SIGNUP_PAGE_NODE_HANDLERS = Object.freeze({
|
||||
'fill-password': (payload) => step3_fillEmailPassword(payload),
|
||||
'fill-profile': (payload) => step5_fillNameBirthday(payload),
|
||||
'oauth-login': (payload) => step6_login(payload),
|
||||
'relogin-bound-email': (payload) => step6_login(payload),
|
||||
'confirm-oauth': (_payload) => step8_findAndClick(),
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||
|
||||
function createExecutor(overrides = {}) {
|
||||
const events = {
|
||||
logs: [],
|
||||
completed: [],
|
||||
fetchCalls: [],
|
||||
};
|
||||
|
||||
const executor = api.createStep10Executor({
|
||||
addLog: async (message, level, options) => {
|
||||
events.logs.push({ message, level, options });
|
||||
},
|
||||
chrome: {},
|
||||
closeConflictingTabsForSource: async () => {},
|
||||
completeNodeFromBackground: async (nodeId, payload) => {
|
||||
events.completed.push({ nodeId, payload });
|
||||
},
|
||||
ensureContentScriptReadyOnTab: async () => {},
|
||||
getPanelMode: () => 'cpa',
|
||||
getStepIdByKeyForState: (stepKey) => ({
|
||||
'oauth-login': 7,
|
||||
'relogin-bound-email': 11,
|
||||
'confirm-oauth': 14,
|
||||
'platform-verify': 15,
|
||||
})[stepKey] || null,
|
||||
getTabId: async () => 1,
|
||||
isLocalhostOAuthCallbackUrl: (url) => {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
return ['localhost', '127.0.0.1'].includes(parsed.hostname)
|
||||
&& Boolean(parsed.searchParams.get('code'))
|
||||
&& Boolean(parsed.searchParams.get('state'));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isTabAlive: async () => true,
|
||||
normalizeCodex2ApiUrl: (url) => url,
|
||||
normalizeSub2ApiUrl: (url) => url,
|
||||
rememberSourceLastUrl: () => {},
|
||||
reuseOrCreateTab: async () => 1,
|
||||
sendToContentScript: async () => ({}),
|
||||
sendToContentScriptResilient: async () => ({}),
|
||||
shouldBypassStep9ForLocalCpa: () => false,
|
||||
SUB2API_STEP9_RESPONSE_TIMEOUT_MS: 30000,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
return { executor, events };
|
||||
}
|
||||
|
||||
test('CPA platform verify resolves relogin tail steps from active definitions', async () => {
|
||||
const originalFetch = global.fetch;
|
||||
const { executor, events } = createExecutor();
|
||||
global.fetch = async (url, options = {}) => {
|
||||
events.fetchCalls.push({ url, body: JSON.parse(options.body || '{}') });
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({ message: 'CPA callback accepted' }),
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
await executor.executeStep10({
|
||||
visibleStep: 15,
|
||||
nodeId: 'platform-verify',
|
||||
localhostUrl: 'http://127.0.0.1:8317/codex/callback?code=abc&state=expected',
|
||||
cpaOAuthState: 'expected',
|
||||
cpaManagementOrigin: 'http://127.0.0.1:8317',
|
||||
vpsUrl: 'http://127.0.0.1:8317/management.html#/oauth',
|
||||
vpsPassword: 'secret',
|
||||
});
|
||||
} finally {
|
||||
global.fetch = originalFetch;
|
||||
}
|
||||
|
||||
assert.equal(events.fetchCalls.length, 1);
|
||||
assert.equal(events.fetchCalls[0].body.redirect_url, 'http://127.0.0.1:8317/codex/callback?code=abc&state=expected');
|
||||
assert.deepStrictEqual(events.completed, [
|
||||
{
|
||||
nodeId: 'platform-verify',
|
||||
payload: {
|
||||
localhostUrl: 'http://127.0.0.1:8317/codex/callback?code=abc&state=expected',
|
||||
verifiedStatus: 'CPA callback accepted',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('CPA state mismatch points relogin-enabled flow back to relogin-bound-email step', async () => {
|
||||
const { executor } = createExecutor();
|
||||
|
||||
await assert.rejects(
|
||||
() => executor.executeStep10({
|
||||
visibleStep: 15,
|
||||
nodeId: 'platform-verify',
|
||||
localhostUrl: 'http://127.0.0.1:8317/codex/callback?code=abc&state=actual',
|
||||
cpaOAuthState: 'expected',
|
||||
cpaManagementOrigin: 'http://127.0.0.1:8317',
|
||||
vpsUrl: 'http://127.0.0.1:8317/management.html#/oauth',
|
||||
vpsPassword: 'secret',
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(error.message, /11/);
|
||||
assert.doesNotMatch(error.message, /步骤\s*10|姝ラ.*10/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('CPA invalid localhost callback points to the dynamic confirm-oauth step', async () => {
|
||||
const { executor } = createExecutor({
|
||||
isLocalhostOAuthCallbackUrl: () => false,
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
() => executor.executeStep10({
|
||||
visibleStep: 15,
|
||||
nodeId: 'platform-verify',
|
||||
localhostUrl: 'notaurl',
|
||||
cpaManagementOrigin: 'http://127.0.0.1:8317',
|
||||
vpsUrl: 'http://127.0.0.1:8317/management.html#/oauth',
|
||||
vpsPassword: 'secret',
|
||||
}),
|
||||
(error) => {
|
||||
assert.match(error.message, /14/);
|
||||
assert.doesNotMatch(error.message, /12/);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user