fix: (regression beta) wrong message failing login

Massimo Melina committed Apr 21, 2025 at 17:47 UTC a2f0f693d43c5e6aa1c7f88d21a1af0c7db452f4
4 files changed +15 -10
e2e/frontend.spec.ts
+7 -1
@@ -15,10 +15,16 @@ test('around1', async ({ page }) => {
15 await page.getByRole('button', { name: 'Login' }).click();
16 await expect(page.getByRole('dialog', {})).toBeVisible();
17 await screenshot(page);
18 - await page.getByRole('textbox', { name: 'Username' }).fill(username);
18 +
19 + await page.getByRole('textbox', { name: 'Username' }).fill(username + '!'); // wrong username
20 await page.getByRole('textbox', { name: 'Username' }).press('Tab');
21 await page.getByRole('textbox', { name: 'Password' }).fill(password);
22 await page.getByRole('button', { name: 'Continue' }).click();
23 + await expect(page.getByText('x!ErrorInvalid credentials')).toBeVisible();
24 + await page.getByRole('alertdialog').getByRole('button', { name: 'Close' }).click();
25 +
26 + await page.getByRole('textbox', { name: 'Username' }).fill(username);
27 + await page.getByRole('button', { name: 'Continue' }).click();
28 await page.locator('div').filter({ hasText: 'Logged in' }).nth(3).click();
29 await screenshot(page);
30 await page.getByRole('button', { name: username }).click();
frontend/src/login.ts
+4 -5
@@ -26,11 +26,10 @@ async function login(username:string, password:string, extra?: object) {
26 state.loginRequired = false
27 return res
28 }, err => {
29 - throw Error(err.message === 'trust' ? t('login_untrusted', "Login aborted: server identity cannot be trusted")
30 - : err.message === 'wrong' ? t('login_bad_credentials', "Invalid credentials")
31 - : err.code === HTTP_UNAUTHORIZED ? t(err.message) // plugin's custom error
32 - : err.code === HTTP_CONFLICT ? t('login_bad_cookies', "Cookies not working - login failed")
33 - : t(err.message || String(err)) )
29 + throw Error(err.data === 'trust' ? t('login_untrusted', "Login aborted: server identity cannot be trusted")
30 + : err.code === HTTP_UNAUTHORIZED && !err.data ? t('login_bad_credentials', "Invalid credentials") // err.data is empty on standard errors, but a plugin may want to show differently
31 + : err.code === HTTP_CONFLICT ? t('login_bad_cookies', "Cookies not working - login failed")
32 + : t(err.message || String(err)) )
33 }).finally(stopWorking)
34 }
35
shared/api.ts
+1 -1
@@ -57,7 +57,7 @@ export function apiCall<T=any>(cmd: string, params?: Dict, options: ApiCallOptio
57 console.debug(res.ok ? 'API' : 'API FAILED', cmd, params??'', '>>', data)
58 await options.onResponse?.(res, data)
59 if (!res.ok)
60 - throw new ApiError(res.status, data === undefined ? body : `Failed API ${cmd}: ${res.statusText}`, data)
60 + throw new ApiError(res.status, data === body ? body : `Failed API ${cmd}: ${res.statusText}`, data)
61 return data as Awaited<T extends (...args: any[]) => infer R ? Awaited<R> : T>
62 }, err => {
63 stop?.()
src/api.auth.ts
+3 -3
@@ -24,7 +24,7 @@ export const login: ApiHandler = async ({ username, password }, ctx) => {
24 try {
25 const account = await clearTextLogin(ctx, username, password, 'api')
26 if (!account)
27 - return new ApiError(HTTP_UNAUTHORIZED, 'wrong')
27 + return new ApiError(HTTP_UNAUTHORIZED)
28 }
29 catch (e) {
30 return new ApiError(HTTP_UNAUTHORIZED, String(e))
@@ -76,7 +76,7 @@ export const loginSrp2: ApiHandler = async ({ pubKey, proof }, ctx) => {
76 return new ApiError(HTTP_NOT_FOUND)
77 try {
78 const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
79 - .catch(() => { throw 'wrong' })
79 + .catch(() => { throw '' })
80 await setLoggedIn(ctx, username)
81 return {
82 proof: String(M2),
@@ -88,7 +88,7 @@ export const loginSrp2: ApiHandler = async ({ pubKey, proof }, ctx) => {
88 ctx.logExtra({ u: username })
89 ctx.state.dontLog = false // log even if log_api is false
90 events.emit('failedLogin', ctx, { username })
91 - return new ApiError(HTTP_UNAUTHORIZED, String(e))
91 + return new ApiError(HTTP_UNAUTHORIZED, e ? String(e) : undefined)
92 }
93 finally {
94 delete ongoingLogins[sid]