Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions background/phone-verification-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -1318,7 +1318,7 @@

function isAuthContentScriptUnreachableError(error) {
const message = String(error?.message || error || '').trim();
return /Receiving end does not exist|Could not establish connection|Frame with ID \d+ is showing error page|等待认证页状态检查超时/i.test(message);
return /Receiving end does not exist|Could not establish connection|Frame with ID \d+ is showing error page|Content script .* did not respond|Try refreshing the tab and retry|等待认证页状态检查超时/i.test(message);
}

function buildPhoneRestartStep7Error(phoneNumber = '') {
Expand Down Expand Up @@ -3296,9 +3296,20 @@
}

async function cancelSignupPhoneActivation(state = {}, activation = null) {
const normalizedActivation = normalizeActivation(activation || state?.signupPhoneActivation);
let latestState = state;
try {
latestState = await getState();
} catch (_) {
latestState = state;
}
const normalizedActivation = normalizeActivation(activation)
|| normalizeActivation(latestState?.signupPhoneActivation)
|| normalizeActivation(state?.signupPhoneActivation);
if (normalizedActivation) {
await cancelPhoneActivation(state, normalizedActivation);
await cancelPhoneActivation({
...(state || {}),
...(latestState || {}),
}, normalizedActivation);
}
await clearSignupPhoneRuntimeState();
}
Expand Down Expand Up @@ -3670,7 +3681,16 @@
throw new Error(`步骤 ${visibleStep}:登录手机验证码未能成功提交。`);
} catch (error) {
if (shouldCancelActivation && activation) {
await cancelPhoneActivation(state, activation).catch(() => {});
let latestState = state;
try {
latestState = await getState();
} catch (_) {
latestState = state;
}
await cancelPhoneActivation({
...(state || {}),
...(latestState || {}),
}, activation).catch(() => {});
}
await setPhoneRuntimeState({
signupPhoneActivation: null,
Expand Down
115 changes: 115 additions & 0 deletions tests/phone-verification-flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8161,6 +8161,121 @@ test('phone verification helper propagates stop errors instead of swallowing res
assert.equal(messages.includes('RETURN_TO_ADD_PHONE'), false);
});

test('phone verification helper releases HeroSMS order before recovering from content script no-response', async () => {
const requests = [];
const messages = [];
let navigateCalls = 0;
let acquireCount = 0;
let currentState = {
heroSmsApiKey: 'demo-key',
heroSmsCountryId: 52,
heroSmsCountryLabel: 'Thailand',
verificationResendCount: 0,
phoneVerificationReplacementLimit: 2,
phoneCodeWaitSeconds: 15,
phoneCodeTimeoutWindows: 1,
phoneCodePollIntervalSeconds: 15,
phoneCodePollMaxRounds: 1,
currentPhoneActivation: null,
reusablePhoneActivation: null,
};

const helpers = api.createPhoneVerificationHelpers({
addLog: async () => {},
ensureStep8SignupPageReady: async () => {},
fetchImpl: async (url) => {
const parsedUrl = new URL(url);
requests.push(parsedUrl);
const action = parsedUrl.searchParams.get('action');
const id = parsedUrl.searchParams.get('id');

if (action === 'getPrices') {
return { ok: true, text: async () => buildHeroSmsPricesPayload() };
}
if (action === 'getNumber') {
acquireCount += 1;
return {
ok: true,
text: async () => (
acquireCount === 1
? 'ACCESS_NUMBER:hero-old:66950000001'
: 'ACCESS_NUMBER:hero-new:66950000002'
),
};
}
if (action === 'getStatus') {
return {
ok: true,
text: async () => (id === 'hero-new' ? 'STATUS_OK:246810' : 'STATUS_WAIT_CODE'),
};
}
if (action === 'setStatus') {
return { ok: true, text: async () => 'STATUS_UPDATED' };
}
throw new Error(`Unexpected HeroSMS action: ${action}`);
},
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
getState: async () => ({ ...currentState }),
navigateAuthTabToAddPhone: async () => {
navigateCalls += 1;
return {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
};
},
sendToContentScriptResilient: async (_source, message) => {
messages.push(message.type);
if (message.type === 'SUBMIT_PHONE_NUMBER') {
return {
phoneVerificationPage: true,
url: 'https://auth.openai.com/phone-verification',
};
}
if (message.type === 'RETURN_TO_ADD_PHONE' || message.type === 'STEP8_GET_STATE') {
throw new Error('Content script on openai-auth did not respond in 30s. Try refreshing the tab and retry.');
}
if (message.type === 'SUBMIT_PHONE_VERIFICATION_CODE') {
return {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
};
}
throw new Error(`Unexpected content-script message: ${message.type}`);
},
setState: async (updates) => {
currentState = { ...currentState, ...updates };
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
});

const result = await helpers.completePhoneVerificationFlow(1, {
addPhonePage: true,
phoneVerificationPage: false,
url: 'https://auth.openai.com/add-phone',
});

assert.deepStrictEqual(result, {
success: true,
consentReady: true,
url: 'https://auth.openai.com/authorize',
});
assert.equal(navigateCalls, 1);
assert.deepStrictEqual(
requests
.filter((requestUrl) => requestUrl.searchParams.get('action') === 'setStatus')
.map((requestUrl) => `${requestUrl.searchParams.get('id')}:${requestUrl.searchParams.get('status')}`),
['hero-old:8', 'hero-new:6']
);
assert.deepStrictEqual(
messages.filter((type) => type === 'SUBMIT_PHONE_NUMBER'),
['SUBMIT_PHONE_NUMBER', 'SUBMIT_PHONE_NUMBER']
);
assert.equal(currentState.currentPhoneActivation, null);
});

test('phone verification helper falls back to the next country after repeated sms timeout on the same country', async () => {
const requests = [];
let currentState = {
Expand Down