From 4ac465f16c220e629de4a0f37f7fb0ee795f99b9 Mon Sep 17 00:00:00 2001 From: Borislav Pantaleev Date: Wed, 28 May 2025 23:59:04 +0300 Subject: [PATCH 1/4] Check base_url server info onBlur --- src/pages/LoginPage.tsx | 59 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index b6ab69b..a485bc8 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -138,48 +138,40 @@ const LoginPage = () => { const [serverVersion, setServerVersion] = useState(""); const [matrixVersions, setMatrixVersions] = useState(""); - const handleUsernameChange = () => { - if (formData.base_url || allowSingleBaseUrl) { - return; - } - // check if username is a full qualified userId then set base_url accordingly - const domain = splitMxid(formData.username)?.domain; - if (domain) { - getWellKnownUrl(domain).then(url => { - if (allowAnyBaseUrl || (allowMultipleBaseUrls && restrictBaseUrl.includes(url))) - form.setValue("base_url", url); - }); - } - }; + const checkServerInfo = (url: string) => { + if (!isValidBaseUrl(url)) return; - useEffect(() => { - if (!formData.base_url) { - form.setValue("base_url", ""); - } - if (formData.base_url === "" && allowMultipleBaseUrls) { - form.setValue("base_url", restrictBaseUrl[0]); - } - if (!isValidBaseUrl(formData.base_url)) return; - - getServerVersion(formData.base_url) + getServerVersion(url) .then(serverVersion => setServerVersion(`${translate("synapseadmin.auth.server_version")} ${serverVersion}`)) .catch(() => setServerVersion("")); - getSupportedFeatures(formData.base_url) + getSupportedFeatures(url) .then(features => setMatrixVersions(`${translate("synapseadmin.auth.supports_specs")} ${features.versions.join(", ")}`) ) .catch(() => setMatrixVersions("")); // Set SSO Url - getSupportedLoginFlows(formData.base_url) + getSupportedLoginFlows(url) .then(loginFlows => { const supportPass = loginFlows.find(f => f.type === "m.login.password") !== undefined; const supportSSO = loginFlows.find(f => f.type === "m.login.sso") !== undefined; setSupportPassAuth(supportPass); - setSSOBaseUrl(supportSSO ? formData.base_url : ""); + setSSOBaseUrl(supportSSO ? url : ""); }) .catch(() => setSSOBaseUrl("")); + }; + + const handleBaseUrlBlur = () => { + // Trigger validation only when user finishes typing + form.trigger("base_url"); + checkServerInfo(formData.base_url); + }; + + useEffect(() => { + if (formData.base_url === "" && allowMultipleBaseUrls) { + form.setValue("base_url", restrictBaseUrl[0]); + } }, [formData.base_url, form]); useEffect(() => { @@ -189,6 +181,7 @@ const LoginPage = () => { const password = params.get("password"); const accessToken = params.get("accessToken"); let serverURL = params.get("server"); + if (username) { form.setValue("username", username); } @@ -202,12 +195,19 @@ const LoginPage = () => { form.setValue("accessToken", accessToken); } } + if (serverURL) { const isFullUrl = serverURL.match(/^(http|https):\/\//); if (!isFullUrl) { serverURL = `https://${serverURL}`; } - form.setValue("base_url", serverURL); + + form.setValue("base_url", serverURL, { + shouldValidate: true, + shouldDirty: true, + }); + + checkServerInfo(serverURL); } }, [window.location.search]); @@ -231,7 +231,7 @@ const LoginPage = () => { source="username" label="ra.auth.username" autoComplete="username" - onBlur={handleUsernameChange} + onBlur={handleBaseUrlBlur} resettable validate={required()} {...(loading || !supportPassAuth ? { disabled: true } : {})} @@ -270,6 +270,7 @@ const LoginPage = () => { readOnly={allowSingleBaseUrl} resettable={allowAnyBaseUrl} validate={[required(), validateBaseUrl]} + onBlur={handleBaseUrlBlur} > {allowMultipleBaseUrls && restrictBaseUrl.map(url => ( @@ -286,7 +287,7 @@ const LoginPage = () => { }; return ( -
+ From f7d46a4cecddb6b51497512e17c41b10ef4d2db2 Mon Sep 17 00:00:00 2001 From: Borislav Pantaleev Date: Thu, 29 May 2025 00:10:05 +0300 Subject: [PATCH 2/4] bring back handleUsernameChange --- src/pages/LoginPage.tsx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index a485bc8..6acb02f 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -138,6 +138,25 @@ const LoginPage = () => { const [serverVersion, setServerVersion] = useState(""); const [matrixVersions, setMatrixVersions] = useState(""); + const handleUsernameChange = () => { + if (formData.base_url || allowSingleBaseUrl) { + return; + } + // check if username is a full qualified userId then set base_url accordingly + const domain = splitMxid(formData.username)?.domain; + if (domain) { + getWellKnownUrl(domain).then(url => { + if (allowAnyBaseUrl || (allowMultipleBaseUrls && restrictBaseUrl.includes(url))) { + form.setValue("base_url", url, { + shouldValidate: true, + shouldDirty: true, + }); + checkServerInfo(url); + } + }); + } + }; + const checkServerInfo = (url: string) => { if (!isValidBaseUrl(url)) return; @@ -231,7 +250,7 @@ const LoginPage = () => { source="username" label="ra.auth.username" autoComplete="username" - onBlur={handleBaseUrlBlur} + onBlur={handleUsernameChange} resettable validate={required()} {...(loading || !supportPassAuth ? { disabled: true } : {})} From 9c1838031c26fbb256c3042766bf40a467839fd8 Mon Sep 17 00:00:00 2001 From: Borislav Pantaleev Date: Tue, 3 Jun 2025 23:37:51 +0300 Subject: [PATCH 3/4] Fix server info version display with restrictBaseUrl values --- src/pages/LoginPage.tsx | 132 +++++++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 6acb02f..cb6ee98 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -23,6 +23,7 @@ import { useTranslate, PasswordInput, TextInput, + SelectInput, useLocales, } from "react-admin"; import { useFormContext } from "react-hook-form"; @@ -51,16 +52,31 @@ const LoginPage = () => { restrictBaseUrl.length > 0 && restrictBaseUrl[0] !== "" && restrictBaseUrl[0] !== null; + const baseUrlChoices = allowMultipleBaseUrls ? restrictBaseUrl.map(url => ({ id: url, name: url })) : []; const allowAnyBaseUrl = !(allowSingleBaseUrl || allowMultipleBaseUrls); + const localStorageBaseUrl = localStorage.getItem("base_url"); + let base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorageBaseUrl; + if (allowMultipleBaseUrls && localStorageBaseUrl && !restrictBaseUrl.includes(localStorageBaseUrl)) { + // don't set base_url if it is not in the restrictBaseUrl array + base_url = null; + } const [loading, setLoading] = useState(false); const [supportPassAuth, setSupportPassAuth] = useState(true); const [locale, setLocale] = useLocaleState(); const locales = useLocales(); const translate = useTranslate(); - const base_url = allowSingleBaseUrl ? restrictBaseUrl : localStorage.getItem("base_url"); + const [ssoBaseUrl, setSSOBaseUrl] = useState(""); const loginToken = new URLSearchParams(window.location.search).get("loginToken"); const [loginMethod, setLoginMethod] = useState("credentials"); + const [serverVersion, setServerVersion] = useState(""); + const [matrixVersions, setMatrixVersions] = useState(""); + + useEffect(() => { + if (base_url) { + checkServerInfo(base_url); + } + }, []); useEffect(() => { if (!loginToken) { @@ -133,66 +149,77 @@ const LoginPage = () => { window.location.href = ssoFullUrl; }; + const checkServerInfo = async (url: string) => { + if (!isValidBaseUrl(url)) { + setServerVersion(""); + setMatrixVersions(""); + setSupportPassAuth(false); + setSSOBaseUrl(""); + return; + } + + try { + const serverVersion = await getServerVersion(url); + setServerVersion(`${translate("synapseadmin.auth.server_version")} ${serverVersion}`); + } catch (error) { + setServerVersion(""); + } + + try { + const features = await getSupportedFeatures(url); + setMatrixVersions(`${translate("synapseadmin.auth.supports_specs")} ${features.versions.join(", ")}`); + } catch (error) { + setMatrixVersions(""); + } + + // Set SSO Url + try { + const loginFlows = await getSupportedLoginFlows(url); + const supportPass = loginFlows.find(f => f.type === "m.login.password") !== undefined; + const supportSSO = loginFlows.find(f => f.type === "m.login.sso") !== undefined; + setSupportPassAuth(supportPass); + setSSOBaseUrl(supportSSO ? url : ""); + } catch (error) { + setSupportPassAuth(false); + setSSOBaseUrl(""); + } + }; + + const UserData = ({ formData }) => { const form = useFormContext(); - const [serverVersion, setServerVersion] = useState(""); - const [matrixVersions, setMatrixVersions] = useState(""); - const handleUsernameChange = () => { + const handleUsernameChange = async () => { if (formData.base_url || allowSingleBaseUrl) { return; } // check if username is a full qualified userId then set base_url accordingly const domain = splitMxid(formData.username)?.domain; if (domain) { - getWellKnownUrl(domain).then(url => { + const url = await getWellKnownUrl(domain); if (allowAnyBaseUrl || (allowMultipleBaseUrls && restrictBaseUrl.includes(url))) { form.setValue("base_url", url, { shouldValidate: true, shouldDirty: true, }); - checkServerInfo(url); - } - }); + checkServerInfo(url); + } } }; - const checkServerInfo = (url: string) => { - if (!isValidBaseUrl(url)) return; + const handleBaseUrlBlurOrChange = (event) => { + // Get the value either from the event (onChange) or from formData (onBlur) + const value = event?.target?.value || formData.base_url; - getServerVersion(url) - .then(serverVersion => setServerVersion(`${translate("synapseadmin.auth.server_version")} ${serverVersion}`)) - .catch(() => setServerVersion("")); + if (!value) { + return; + } - getSupportedFeatures(url) - .then(features => - setMatrixVersions(`${translate("synapseadmin.auth.supports_specs")} ${features.versions.join(", ")}`) - ) - .catch(() => setMatrixVersions("")); - - // Set SSO Url - getSupportedLoginFlows(url) - .then(loginFlows => { - const supportPass = loginFlows.find(f => f.type === "m.login.password") !== undefined; - const supportSSO = loginFlows.find(f => f.type === "m.login.sso") !== undefined; - setSupportPassAuth(supportPass); - setSSOBaseUrl(supportSSO ? url : ""); - }) - .catch(() => setSSOBaseUrl("")); - }; - - const handleBaseUrlBlur = () => { - // Trigger validation only when user finishes typing + // Trigger validation only when user finishes typing/selecting form.trigger("base_url"); - checkServerInfo(formData.base_url); + checkServerInfo(value); }; - useEffect(() => { - if (formData.base_url === "" && allowMultipleBaseUrls) { - form.setValue("base_url", restrictBaseUrl[0]); - } - }, [formData.base_url, form]); - useEffect(() => { const params = new URLSearchParams(window.location.search); const hostname = window.location.hostname; @@ -280,24 +307,29 @@ const LoginPage = () => { )} - + )} + {!allowMultipleBaseUrls && ( - {allowMultipleBaseUrls && - restrictBaseUrl.map(url => ( - - {url} - - ))} - + onBlur={handleBaseUrlBlurOrChange} + /> + )} {serverVersion} {matrixVersions} From 273e7b11adacb4023361e4995bc87b509d6d4bce Mon Sep 17 00:00:00 2001 From: Aine Date: Tue, 3 Jun 2025 23:57:55 +0300 Subject: [PATCH 4/4] update readme; remove autofocus from username field (login form) --- README.md | 1 + src/pages/LoginPage.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be3c56c..bca0478 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ The following changes are already implemented: * ๐Ÿ—‚๏ธ [Add Users' Account Data tab](https://github.com/etkecc/synapse-admin/pull/276) * ๐Ÿงพ [Make bulk registration CSV import more user-friendly](https://github.com/etkecc/synapse-admin/pull/411) * ๐ŸŒ [Configurable CORS Credentials](https://github.com/etkecc/synapse-admin/pull/456) +* [Do not check homeserver URL during typing in the login form](https://github.com/etkecc/synapse-admin/pull/585) #### exclusive for [etke.cc](https://etke.cc) customers diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index cb6ee98..5ac3f39 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -273,7 +273,6 @@ const LoginPage = () => { <>