fix: restrict password login to existing phone accounts

This commit is contained in:
2026-04-26 01:11:45 +08:00
parent c4b9b8173f
commit 0a0f3f1bd8
33 changed files with 489 additions and 778 deletions

View File

@@ -13,7 +13,6 @@ const authMocks = vi.hoisted(() => ({
authEntry: vi.fn(),
changePassword: vi.fn(),
ensureStoredAccessToken: vi.fn(),
ensureAutoAuthUser: vi.fn(),
getAuthLoginOptions: vi.fn(),
getCurrentAuthUser: vi.fn(),
loginWithPhoneCode: vi.fn(),
@@ -36,7 +35,6 @@ vi.mock('../../services/authService', () => ({
changePassword: authMocks.changePassword,
changePhoneNumber: vi.fn(),
consumeAuthCallbackResult: authMocks.consumeAuthCallbackResult,
ensureAutoAuthUser: authMocks.ensureAutoAuthUser,
getStoredLastLoginPhone: vi.fn(() => ''),
getAuthAuditLogs: vi.fn(),
getAuthLoginOptions: authMocks.getAuthLoginOptions,
@@ -106,16 +104,13 @@ beforeEach(() => {
expiresInSeconds: 300,
});
authMocks.startWechatLogin.mockResolvedValue(undefined);
authMocks.ensureAutoAuthUser.mockResolvedValue({
user: mockUser,
credentials: {
username: 'guest_tester',
password: 'auto_password',
},
});
});
function ProtectedActionButton({ onAuthenticated }: { onAuthenticated: () => void }) {
function ProtectedActionButton({
onAuthenticated,
}: {
onAuthenticated: () => void;
}) {
const authUi = useAuthUi();
return (
@@ -178,7 +173,6 @@ test('auth gate keeps platform content visible when phone login is available', a
expect(await screen.findByText('应用内容')).toBeTruthy();
expect(screen.queryByRole('button', { name: '登录' })).toBeNull();
expect(screen.queryByText('先登录账号,再同步你的冒险进度。')).toBeNull();
expect(authMocks.ensureAutoAuthUser).not.toHaveBeenCalled();
});
test('auth gate waits for access token refresh before exposing restored user content', async () => {
@@ -220,7 +214,6 @@ test('auth gate does not auto-create a guest account when dev guest switch is no
);
expect(await screen.findByText('应用内容')).toBeTruthy();
expect(authMocks.ensureAutoAuthUser).not.toHaveBeenCalled();
});
test('auth gate opens a login modal for protected actions and resumes after login', async () => {
@@ -245,7 +238,7 @@ test('auth gate opens a login modal for protected actions and resumes after logi
await user.type(within(dialog).getByLabelText('手机号'), '13800000000');
await user.type(within(dialog).getByLabelText('验证码'), '123456');
await user.click(within(dialog).getByRole('button', { name: '注册/登录' }));
await user.click(within(dialog).getByRole('button', { name: '登录' }));
await waitFor(() => {
expect(authMocks.loginWithPhoneCode).toHaveBeenCalledWith(
@@ -388,24 +381,26 @@ test('login modal resets draft state every time it is reopened', async () => {
const firstDialog = screen.getByRole('dialog', { name: '账号入口' });
await user.type(within(firstDialog).getByLabelText('手机号'), '13800000000');
await user.click(within(firstDialog).getByRole('button', { name: '获取验证码' }));
await user.click(
within(firstDialog).getByRole('button', { name: '获取验证码' }),
);
expect(
await within(firstDialog).findByText('短信请求已提交,验证码有效期约 5 分钟。'),
await within(firstDialog).findByText(
'短信请求已提交,验证码有效期约 5 分钟。',
),
).toBeTruthy();
await user.type(within(firstDialog).getByLabelText('验证码'), '123456');
await user.click(within(firstDialog).getByRole('tab', { name: '密码登录' }));
await user.type(within(firstDialog).getByLabelText('密码'), 'passw0rd');
await user.click(within(firstDialog).getByRole('button', { name: '忘记密码' }));
expect(
screen.getByRole('dialog', { name: '重置密码' }),
).toBeTruthy();
await user.click(
screen.getByRole('button', { name: '关闭登录弹窗' }),
within(firstDialog).getByRole('button', { name: '忘记密码' }),
);
expect(screen.getByRole('dialog', { name: '重置密码' })).toBeTruthy();
await user.click(screen.getByRole('button', { name: '关闭登录弹窗' }));
await waitFor(() => {
expect(screen.queryByRole('dialog', { name: '账号入口' })).toBeNull();
});
@@ -426,7 +421,9 @@ test('login modal resets draft state every time it is reopened', async () => {
).toBe('');
expect(within(reopenedDialog).queryByLabelText('密码')).toBeNull();
expect(
within(reopenedDialog).queryByText('短信请求已提交,验证码有效期约 5 分钟。'),
within(reopenedDialog).queryByText(
'短信请求已提交,验证码有效期约 5 分钟。',
),
).toBeNull();
expect(
within(reopenedDialog).getByRole('button', { name: '获取验证码' }),
@@ -465,9 +462,9 @@ test('auth gate separates sms and password login by tabs', async () => {
).toBe('true');
expect(within(dialog).queryByLabelText('验证码')).toBeNull();
await user.type(within(dialog).getByLabelText('手机号/邮箱'), '13800000000');
await user.type(within(dialog).getByLabelText('手机号'), '13800000000');
await user.type(within(dialog).getByLabelText('密码'), 'passw0rd');
await user.click(within(dialog).getByRole('button', { name: '注册/登录' }));
await user.click(within(dialog).getByRole('button', { name: '登录' }));
await waitFor(() => {
expect(authMocks.authEntry).toHaveBeenCalledWith('13800000000', 'passw0rd');