fix: chrome was prompting to save credentials without username because of login's double-form
Massimo Melina committed
Jan 31, 2023 at 22:02 UTC
ded3b70114520c08918c31d006773214611cdf98
3 files changed
+86
-21
frontend/src/index.scss
+13
@@ -281,6 +281,19 @@ button label {
281
& td:nth-child(3) { padding: .2em .5em; word-break: break-word; }
282
}
283
284
+.dialog-login {
285
+ form {
286
+ display: flex;
287
+ flex-direction: column;
288
+ gap: 1.2em;
289
+ }
290
+ label {
291
+ display: block;
292
+ margin-bottom: .5em;
293
+ margin-left: .1em;
294
+ }
295
+}
296
+
297
/* Works on Firefox */
298
* {
299
scrollbar-width: thin;
frontend/src/login.ts
+73
-20
@@ -1,10 +1,11 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
-import { apiCall, ApiError } from './api'
3
+import { apiCall } from './api'
4
import { state } from './state'
5
-import { alertDialog, promptDialog } from './dialog'
5
+import { alertDialog, newDialog } from './dialog'
6
import { hIcon, srpSequence, working } from './misc'
7
import { useNavigate } from 'react-router-dom'
8
+import { createElement as h, useEffect, useRef } from 'react'
9
10
async function login(username:string, password:string) {
11
const stopWorking = working()
@@ -13,16 +14,12 @@ async function login(username:string, password:string) {
14
sessionRefresher(res)
15
state.loginRequired = false
16
return res
16
- }, (err: Error) => {
17
+ }, (err: any) => {
18
stopWorking()
18
- if (err.message === 'trust')
19
- err = Error("Login aborted: server identity cannot be trusted")
20
- else if (err instanceof ApiError)
21
- if (err.code === 401)
22
- err = Error("Invalid credentials")
23
- else if (err.code === 409)
24
- err = Error("Cookies not working - login failed")
25
- return alertDialog(err)
19
+ throw Error(err.message === 'trust' ? "Login aborted: server identity cannot be trusted"
20
+ : err.code === 401 ? "Invalid credentials"
21
+ : err.code === 409 ? "Cookies not working - login failed"
22
+ : err.message)
23
})
24
}
25
@@ -51,13 +48,69 @@ export function logout(){
48
}
49
50
export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
54
- const title = "Login"
55
- const icon = () => hIcon('login')
56
- const user = await promptDialog('Username', { title, icon })
57
- if (!user) return
58
- const password = await promptDialog('Password', { type: 'password', title, icon })
59
- if (!password) return
60
- const res = await login(user, password)
61
- if (res?.redirect)
62
- navigate(res.redirect)
51
+ return new Promise(resolve => {
52
+ const closeDialog = newDialog({
53
+ className: 'dialog-login',
54
+ icon: () => hIcon('login'),
55
+ onClose: resolve,
56
+ title: "Login",
57
+ Content() {
58
+ const usrRef = useRef<HTMLInputElement>()
59
+ const pwdRef = useRef<HTMLInputElement>()
60
+ useEffect(() => {
61
+ setTimeout(() => usrRef.current?.focus()) // setTimeout workarounds problem due to double-mount while in dev
62
+ }, [])
63
+ return h('form', {},
64
+ h('div', { className: 'field' },
65
+ h('label', { htmlFor: 'username' }, "Username"),
66
+ h('input', {
67
+ ref: usrRef,
68
+ name: 'username',
69
+ autoComplete: 'username',
70
+ required: true,
71
+ onKeyDown
72
+ }),
73
+ ),
74
+ h('div', { className: 'field' },
75
+ h('label', { htmlFor: 'password' }, "Password"),
76
+ h('input', {
77
+ ref: pwdRef,
78
+ name: 'password',
79
+ type: 'password',
80
+ autoComplete: 'current-password',
81
+ required: true,
82
+ onKeyDown
83
+ }),
84
+ ),
85
+ h('div', { style: { textAlign: 'right' } },
86
+ h('button', { onClick: go }, "Continue")),
87
+ )
88
+
89
+ function onKeyDown(ev: KeyboardEvent) {
90
+ const { key } = ev
91
+ if (key === 'Escape')
92
+ return closeDialog(null)
93
+ if (key === 'Enter')
94
+ return go()
95
+ }
96
+
97
+ async function go(ev?: Event) {
98
+ ev?.stopPropagation()
99
+ const usr = usrRef.current?.value
100
+ const pwd = pwdRef.current?.value
101
+ if (!usr || !pwd) return
102
+ try {
103
+ const res = await login(usr, pwd)
104
+ closeDialog()
105
+ if (res?.redirect)
106
+ navigate(res.redirect)
107
+ } catch (err: any) {
108
+ await alertDialog(err)
109
+ usrRef.current?.focus()
110
+ }
111
+ }
112
+
113
+ }
114
+ })
115
+ })
116
}
todo.md
-1
@@ -3,7 +3,6 @@
3
- plugins: after installing, switch to installed (and perhaps highlight new one)
4
- plugins' log, accessible in admin
5
- fix: cannot switch off https and switch on http at the same time
6
-- fix: chrome is prompting to save credentials without username because of login's double-form
6
- admin/monitor: show user-agent
7
- admin: check + update
8
- frontend: hide closer button on login dialog accessing a protected resource, as it's no use