@samitouri / QOSami-HFS / commits / ad1b063d

plugins: finalizingLogin #328

Massimo Melina committed Feb 21, 2025 at 19:39 UTC ad1b063d7db30c2aa789c6f850f063846debfe08
4 files changed +34 -15
dev-plugins.md
+7
@@ -561,6 +561,11 @@ This section is still partially documented, and you may need to have a look at t
561 - async supported
562 - preventable
563 - `failedLogin`
564 +- `finalizingLogin`
565 + - parameters: { ctx, username, inputs }
566 + - inputs: object
567 + - merge of all inputs both from body and URL
568 + - async supported
569 - `config ready`
570 - `config.KEY` where KEY is the key of a config that has changed
571 - `connectionClosed`
@@ -772,6 +777,8 @@ If you want to override a text regardless of the language, use the special langu
777
778 ## API version history
779
780 +- 12.0 (v0.57.0)
781 + - backend event: finalizingLogin
782 - 11.6 (v0.56.0)
783 - api.setError
784 - frontend events: afterBreadcrumbs, afterFolderStats, afterFilter
frontend/src/login.ts
+17 -12
@@ -26,9 +26,10 @@ async function login(username:string, password:string, extra?: object) {
26 return res
27 }, err => {
28 throw Error(err.message === 'trust' ? t('login_untrusted', "Login aborted: server identity cannot be trusted")
29 - : err.code === HTTP_UNAUTHORIZED ? t('login_bad_credentials', "Invalid credentials")
30 - : err.code === HTTP_CONFLICT ? t('login_bad_cookies', "Cookies not working - login failed")
31 - : t(err.message || String(err)))
29 + : err.message === 'wrong' ? t('login_bad_credentials', "Invalid credentials")
30 + : err.code === HTTP_UNAUTHORIZED ? t(err.message) // plugin's custom error
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
@@ -75,7 +76,7 @@ export async function loginDialog(closable=true, reloadAfter=true) {
76 return h('form', {
77 onSubmit(ev:any) {
78 ev.preventDefault()
78 - go()
79 + go(ev)
80 }
81 },
82 h(CustomCode, { name: 'beforeLogin' }),
@@ -115,18 +116,22 @@ export async function loginDialog(closable=true, reloadAfter=true) {
116 if (key === 'Escape')
117 return close(null)
118 if (key === 'Enter')
118 - return go()
119 + return go(ev)
120 }
121
121 - async function go(ev?: Event) {
122 - ev?.stopPropagation()
123 - const usr = usrRef.current?.value.trim()
124 - const pwd = pwdRef.current?.value
125 - if (going || !usr || !pwd) return
122 + async function go(ev: Event) {
123 + const form = ev.target instanceof HTMLElement && ev.target.closest('form')
124 + if (!form) return
125 + ev.stopPropagation()
126 + const { username, password, ...rest } = Object.fromEntries(Array.from(form.querySelectorAll('[name]'))
127 + .map(el => el instanceof HTMLInputElement ? [el.name, el.value] : []))
128 + const u = username.trim()
129 + if (going || !u || !password) return
130 going = true
131 try {
128 - const res = await login(usr, pwd, {
129 - [CFG.allow_session_ip_change]: ipRef.current?.checked
132 + const res = await login(u, password, {
133 + [CFG.allow_session_ip_change]: ipRef.current?.checked,
134 + ...rest
135 })
136 await close(true)
137 toast(t`Logged in`, 'success')
src/api.auth.ts
+9 -3
@@ -21,9 +21,14 @@ export const login: ApiHandler = async ({ username, password }, ctx) => {
21 return new ApiError(HTTP_BAD_REQUEST)
22 if (!ctx.session)
23 return new ApiError(HTTP_SERVER_ERROR)
24 - const account = await clearTextLogin(ctx, username, password, 'api')
25 - if (!account)
26 - return new ApiError(HTTP_UNAUTHORIZED)
24 + try {
25 + const account = await clearTextLogin(ctx, username, password, 'api')
26 + if (!account)
27 + return new ApiError(HTTP_UNAUTHORIZED, 'wrong')
28 + }
29 + catch (e) {
30 + return new ApiError(HTTP_UNAUTHORIZED, String(e))
31 + }
32 return {
33 redirect: ctx.state.account?.redirect,
34 ...await refresh_session({},ctx)
@@ -71,6 +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' })
80 await setLoggedIn(ctx, username)
81 return {
82 proof: String(M2),
src/auth.ts
+1
@@ -64,6 +64,7 @@ export async function setLoggedIn(ctx: Context, username: string | false) {
64 }
65 const a = ctx.state.account = getAccount(username)
66 if (!a) return
67 + await events.emitAsync('finalizingLogin', { ctx, username, inputs: { ...ctx.state.params, ...ctx.query } })
68 s.username = normalizeUsername(username)
69 s.ts = Date.now()
70 const k = CFG.allow_session_ip_change