fix(auth): preserve phone identity across add-email retries

This commit is contained in:
InkCrow
2026-05-12 20:39:20 +08:00
parent 0b74e600c0
commit 67c111ed43
7 changed files with 183 additions and 2 deletions
@@ -244,6 +244,31 @@ test('message router preserves phone signup identity when step payload only repo
assert.ok(events.stateUpdates.some((updates) => updates.signupVerificationRequestedAt === 123456));
});
test('message router persists phone signup identity from step 7 completion payload', async () => {
const completedActivation = {
activationId: 'signup-done',
phoneNumber: '+5511917097811',
};
const { router, events } = createRouter({
state: { stepStatuses: { 7: 'completed', 8: 'pending' } },
});
await router.handleStepData(7, {
loginVerificationRequestedAt: 123456,
accountIdentifierType: 'phone',
accountIdentifier: '+5511917097811',
signupPhoneNumber: '+5511917097811',
signupPhoneActivation: null,
signupPhoneCompletedActivation: completedActivation,
});
assert.deepStrictEqual(events.signupPhoneSilentStates, ['+5511917097811']);
assert.ok(events.stateUpdates.some((updates) => updates.signupPhoneActivation === null));
assert.ok(events.stateUpdates.some((updates) => updates.signupPhoneCompletedActivation === completedActivation));
assert.ok(events.stateUpdates.some((updates) => updates.loginVerificationRequestedAt === 123456));
assert.deepStrictEqual(events.emailStates, []);
});
test('message router does not overwrite a completed step 3 when step 2 is replayed', async () => {
const { router, events } = createRouter({
state: { stepStatuses: { 3: 'completed' } },
+27 -1
View File
@@ -463,12 +463,15 @@ test('step 7 forwards phone login identity payload when account identifier is ph
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep7;`)(globalScope);
const events = {
completions: [],
payloads: [],
};
const executor = api.createStep7Executor({
addLog: async () => {},
completeStepFromBackground: async () => {},
completeStepFromBackground: async (step, payload) => {
events.completions.push({ step, payload });
},
getErrorMessage: (error) => error?.message || String(error || ''),
getLoginAuthStateLabel: (state) => state || 'unknown',
getState: async () => ({
@@ -524,6 +527,24 @@ test('step 7 forwards phone login identity payload when account identifier is ph
visibleStep: 7,
},
]);
assert.deepStrictEqual(events.completions, [
{
step: 7,
payload: {
loginVerificationRequestedAt: 123456,
accountIdentifierType: 'phone',
accountIdentifier: '66959916439',
signupPhoneNumber: '66959916439',
signupPhoneCompletedActivation: {
activationId: 'signup-done',
phoneNumber: '66959916439',
countryId: 52,
countryLabel: 'Thailand',
},
signupPhoneActivation: null,
},
},
]);
});
test('step 7 keeps Plus email login even when phone sms runtime exists', async () => {
@@ -737,6 +758,11 @@ test('step 7 can start from a manually filled signup phone without completed ste
step: 7,
payload: {
loginVerificationRequestedAt: 987654,
accountIdentifierType: 'phone',
accountIdentifier: '+447780579093',
signupPhoneNumber: '+447780579093',
signupPhoneCompletedActivation: null,
signupPhoneActivation: null,
},
},
]);
+100
View File
@@ -472,6 +472,106 @@ test('step 8 reruns step 7 with preserved phone login identity after add-email v
assert.equal(calls.rerunStates[0].signupPhoneNumber, '+447780579093');
});
test('step 8 add-email rereads persisted phone identity before rerunning step 7', async () => {
const calls = {
resolveCalls: 0,
rerunStates: [],
};
let runtimeState = {
visibleStep: 8,
email: '',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
accountIdentifierType: 'email',
accountIdentifier: 'stale@example.com',
signupMethod: 'phone',
resolvedSignupMethod: 'phone',
phoneVerificationEnabled: true,
signupPhoneNumber: '',
};
const executor = api.createStep8Executor({
addLog: async () => {},
chrome: {
tabs: {
update: async () => {},
},
},
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
confirmCustomVerificationStepBypass: async () => {},
ensureStep8VerificationPageReady: async () => ({ state: 'add_email_page', url: 'https://auth.openai.com/add-email' }),
getOAuthFlowRemainingMs: async () => 8000,
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
getMailConfig: () => ({
provider: 'qq',
label: 'QQ 邮箱',
source: 'mail-qq',
url: 'https://mail.qq.com',
navigateOnReuse: false,
}),
getState: async () => ({ ...runtimeState }),
getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
isVerificationMailPollingError: () => false,
LUCKMAIL_PROVIDER: 'luckmail-api',
persistRegistrationEmailState: async (_state, email, options) => {
assert.equal(email, 'new.user@example.com');
assert.equal(options.preserveAccountIdentity, true);
runtimeState = {
...runtimeState,
email,
accountIdentifierType: 'phone',
accountIdentifier: '+447780579093',
signupPhoneNumber: '+447780579093',
signupPhoneCompletedActivation: {
activationId: 'signup-done',
phoneNumber: '+447780579093',
},
};
},
resolveSignupEmailForFlow: async (_state, options = {}) => {
assert.equal(options.preserveAccountIdentity, true);
return 'new.user@example.com';
},
resolveVerificationStep: async (_step, state) => {
calls.resolveCalls += 1;
assert.equal(state.accountIdentifierType, 'phone');
assert.equal(state.signupPhoneNumber, '+447780579093');
throw new Error('STEP8_RESTART_STEP7::step 8 verification page fell into login timeout retry state');
},
rerunStep7ForStep8Recovery: async () => {
calls.rerunStates.push({ ...runtimeState });
},
reuseOrCreateTab: async () => {},
sendToContentScriptResilient: async () => ({
submitted: true,
displayedEmail: 'new.user@example.com',
url: 'https://auth.openai.com/email-verification',
}),
setState: async (payload) => {
runtimeState = {
...runtimeState,
...payload,
};
},
shouldUseCustomRegistrationEmail: () => false,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 2,
throwIfStopped: () => {},
});
await assert.rejects(
() => executor.executeStep8({ ...runtimeState }),
/STEP8_RESTART_STEP7::/
);
assert.equal(calls.resolveCalls, 2);
assert.equal(calls.rerunStates.length, 1);
assert.equal(calls.rerunStates[0].accountIdentifierType, 'phone');
assert.equal(calls.rerunStates[0].signupPhoneNumber, '+447780579093');
});
test('step 8 email_in_use recovery preserves the previous registration baseline', async () => {
const calls = {
contentCalls: 0,