锁定微信登录失败提示

微信登录和绑手机号失败不再向页面透出原生或后端错误细节

微信 web-view auth 测试覆盖登录失败和绑手机号失败稳定提示

native shell 总门禁反查微信 auth 失败边界
This commit is contained in:
2026-06-20 13:26:45 +08:00
parent c49bccc269
commit f641d2b25f
3 changed files with 119 additions and 24 deletions
+18 -24
View File
@@ -24,6 +24,8 @@ const AUTH_RESULT_STORAGE_KEY = 'genarrative:mini-program-auth-result';
const AUTH_ACTION_LOGIN = 'login';
const PAY_RESULT_RECHECK_DELAY_MS = 120;
const WEB_VIEW_SHARE_TITLE = '陶泥儿';
const WECHAT_LOGIN_UNAVAILABLE_MESSAGE = '微信登录失败,请稍后重试。';
const WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE = '绑定手机号失败,请稍后重试。';
function showWebViewShareMenu() {
if (typeof wx.showShareMenu !== 'function') {
@@ -281,10 +283,12 @@ function wxLogin() {
resolve(result.code);
return;
}
reject(new Error('微信登录未返回 code'));
console.error('[web-view] wx.login returned no code', result);
reject(new Error(WECHAT_LOGIN_UNAVAILABLE_MESSAGE));
},
fail(error) {
reject(new Error(error.errMsg || '微信登录失败'));
console.error('[web-view] wx.login failed', error);
reject(new Error(WECHAT_LOGIN_UNAVAILABLE_MESSAGE));
},
});
});
@@ -320,16 +324,12 @@ function requestMiniProgramLogin(code, displayName) {
resolve(response.data);
return;
}
const message =
response.data &&
response.data.error &&
response.data.error.message
? response.data.error.message
: `微信登录失败:${response.statusCode}`;
reject(new Error(message));
console.error('[web-view] mini program login failed', response);
reject(new Error(WECHAT_LOGIN_UNAVAILABLE_MESSAGE));
},
fail(error) {
reject(new Error(error.errMsg || '微信登录请求失败'));
console.error('[web-view] mini program login request failed', error);
reject(new Error(WECHAT_LOGIN_UNAVAILABLE_MESSAGE));
},
});
});
@@ -366,16 +366,12 @@ function requestMiniProgramBindPhone(authToken, wechatPhoneCode, displayName) {
resolve(response.data);
return;
}
const message =
response.data &&
response.data.error &&
response.data.error.message
? response.data.error.message
: `绑定手机号失败:${response.statusCode}`;
reject(new Error(message));
console.error('[web-view] mini program bind phone failed', response);
reject(new Error(WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE));
},
fail(error) {
reject(new Error(error.errMsg || '绑定手机号请求失败'));
console.error('[web-view] mini program bind phone request failed', error);
reject(new Error(WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE));
},
});
});
@@ -542,10 +538,10 @@ function createWechatWebViewPage() {
webViewUrl: resolveWebViewUrl(authResult, this._lastLaunchQuery || {}),
});
} catch (error) {
console.error('[web-view] auth flow failed', error);
this.setData({
authResult: null,
errorMessage:
error && error.message ? error.message : '微信登录失败,请稍后重试。',
errorMessage: WECHAT_LOGIN_UNAVAILABLE_MESSAGE,
loggingIn: false,
loading: false,
nicknameRequired: false,
@@ -647,12 +643,10 @@ function createWechatWebViewPage() {
),
});
} catch (error) {
console.error('[web-view] bind phone failed', error);
this.setData({
bindingPhone: false,
errorMessage:
error && error.message
? error.message
: '绑定手机号失败,请稍后重试。',
errorMessage: WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE,
});
}
},
+49
View File
@@ -1942,6 +1942,52 @@ function assertWechatSubscribeResultBoundaries() {
}
}
function assertWechatAuthFailureBoundaries() {
const webViewShellSource = fs.readFileSync(
'miniprogram/shell/webView.js',
'utf8',
);
const authTestSource = fs.readFileSync(
'scripts/miniprogram-web-view-auth.test.ts',
'utf8',
);
for (const snippet of [
"WECHAT_LOGIN_UNAVAILABLE_MESSAGE = '微信登录失败,请稍后重试。'",
"WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE = '绑定手机号失败,请稍后重试。'",
"console.error('[web-view] wx.login failed', error)",
"console.error('[web-view] mini program login request failed', error)",
"console.error('[web-view] mini program bind phone failed', response)",
'errorMessage: WECHAT_LOGIN_UNAVAILABLE_MESSAGE',
'errorMessage: WECHAT_BIND_PHONE_UNAVAILABLE_MESSAGE',
]) {
if (!webViewShellSource.includes(snippet)) {
throw new Error(`wechat auth shell must include ${snippet}`);
}
}
for (const forbiddenSnippet of [
"reject(new Error(error.errMsg || '微信登录失败'))",
"reject(new Error(error.errMsg || '微信登录请求失败'))",
"reject(new Error(error.errMsg || '绑定手机号请求失败'))",
"error && error.message ? error.message : '微信登录失败,请稍后重试。'",
'response.data.error.message',
]) {
if (webViewShellSource.includes(forbiddenSnippet)) {
throw new Error(`wechat auth shell must not expose native failure detail via ${forbiddenSnippet}`);
}
}
for (const snippet of [
'微信登录失败不向页面透出原生错误',
'绑定手机号失败不向页面透出后端错误体',
"expect(page.data.errorMessage).toBe('微信登录失败,请稍后重试。')",
"expect(page.data.errorMessage).toBe('绑定手机号失败,请稍后重试。')",
]) {
if (!authTestSource.includes(snippet)) {
throw new Error(`wechat auth boundary test must include ${snippet}`);
}
}
}
function assertHostBridgeLayerLayout() {
assertSameList(
readDirectoryFileList(
@@ -2175,6 +2221,9 @@ assertWechatPaymentResultBoundaries();
console.log('[check:native-shells] wechat-subscribe-result-boundaries');
assertWechatSubscribeResultBoundaries();
console.log('[check:native-shells] wechat-auth-failure-boundaries');
assertWechatAuthFailureBoundaries();
console.log('[check:native-shells] h5-host-bridge-event-subscription-gates');
assertH5HostBridgeEventSubscriptionGates();
+52
View File
@@ -113,6 +113,7 @@ function loadCommonJsModule(
describe('mini-program web-view auth page', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, 'error').mockImplementation(() => {});
});
test('默认进入时不预登录,直接打开未登录 web-view', async () => {
@@ -303,4 +304,55 @@ describe('mini-program web-view auth page', () => {
expect(page.data.loading).toBe(false);
expect(page.data.phoneBindingRequired).toBe(true);
});
test('微信登录失败不向页面透出原生错误', async () => {
const wxMock = createWxMock();
const loginError = { errMsg: 'login:fail private native detail' };
wxMock.login.mockImplementation(({ fail }) => {
fail(loginError);
});
const page = loadWebViewPage(wxMock);
await page.onLoad({ authAction: 'login', returnTo: 'previous' });
expect(page.data.errorMessage).toBe('微信登录失败,请稍后重试。');
expect(console.error).toHaveBeenCalledWith(
'[web-view] wx.login failed',
loginError,
);
expect(page.data.phoneBindingRequired).toBe(false);
});
test('绑定手机号失败不向页面透出后端错误体', async () => {
const wxMock = createWxMock();
const page = loadWebViewPage(wxMock);
page.data.authResult = {
token: 'jwt-pending-wechat',
bindingStatus: 'pending_bind_phone',
};
wxMock.request.mockImplementation(({ success }) => {
success({
statusCode: 500,
data: {
error: {
message: 'private backend detail',
},
},
});
});
await page.handleGetPhoneNumber({
detail: {
code: 'wechat-phone-code',
},
});
expect(page.data.errorMessage).toBe('绑定手机号失败,请稍后重试。');
expect(console.error).toHaveBeenCalledWith(
'[web-view] mini program bind phone failed',
expect.objectContaining({
statusCode: 500,
}),
);
});
});