diff --git a/src/components/LoginPage.js b/src/components/LoginPage.js index 6c439a5..1a78944 100644 --- a/src/components/LoginPage.js +++ b/src/components/LoginPage.js @@ -78,11 +78,48 @@ const LoginPage = ({ theme }) => { const login = useLogin(); const notify = useNotify(); const [loading, setLoading] = useState(false); + const [supportPassAuth, setSupportPassAuth] = useState(true); var locale = useLocale(); const setLocale = useSetLocale(); const translate = useTranslate(); const base_url = localStorage.getItem("base_url"); const cfg_base_url = process.env.REACT_APP_SERVER; + const [ssoBaseUrl, setSSOBaseUrl] = useState(""); + const loginToken = /\?loginToken=([a-zA-Z0-9_-]+)/.exec(window.location.href); + + if (loginToken) { + const ssoToken = loginToken[1]; + console.log("SSO token is", ssoToken); + // Prevent further requests + window.history.replaceState( + {}, + "", + window.location.href.replace(loginToken[0], "#").split("#")[0] + ); + const baseUrl = localStorage.getItem("sso_base_url"); + localStorage.removeItem("sso_base_url"); + if (baseUrl) { + const auth = { + base_url: baseUrl, + username: null, + password: null, + loginToken: ssoToken, + }; + console.log("Base URL is:", baseUrl); + console.log("SSO Token is:", ssoToken); + console.log("Let's try token login..."); + login(auth).catch(error => { + alert( + typeof error === "string" + ? error + : typeof error === "undefined" || !error.message + ? "ra.auth.sign_in_error" + : error.message + ); + console.error(error); + }); + } + } const renderInput = ({ meta: { touched, error } = {}, @@ -137,6 +174,14 @@ const LoginPage = ({ theme }) => { }); }; + const handleSSO = () => { + localStorage.setItem("sso_base_url", ssoBaseUrl); + const ssoFullUrl = `${ssoBaseUrl}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent( + window.location.href + )}`; + window.location.href = ssoFullUrl; + }; + const extractHomeServer = username => { const usernameRegex = /@[a-zA-Z0-9._=\-/]+:([a-zA-Z0-9\-.]+\.[a-zA-Z]+)/; if (!username) return null; @@ -188,6 +233,31 @@ const LoginPage = ({ theme }) => { .catch(_ => { setServerVersion(""); }); + + // Set SSO Url + const authMethodUrl = `${formData.base_url}/_matrix/client/r0/login`; + let supportPass = false, + supportSSO = false; + fetchUtils + .fetchJson(authMethodUrl, { method: "GET" }) + .then(({ json }) => { + json.flows.forEach(f => { + if (f.type === "m.login.password") { + supportPass = true; + } else if (f.type === "m.login.sso") { + supportSSO = true; + } + }); + setSupportPassAuth(supportPass); + if (supportSSO) { + setSSOBaseUrl(formData.base_url); + } else { + setSSOBaseUrl(""); + } + }) + .catch(_ => { + setSSOBaseUrl(""); + }); }, [formData.base_url] ); @@ -200,7 +270,7 @@ const LoginPage = ({ theme }) => { name="username" component={renderInput} label={translate("ra.auth.username")} - disabled={loading} + disabled={loading || !supportPassAuth} onBlur={handleUsernameChange} resettable fullWidth @@ -212,7 +282,7 @@ const LoginPage = ({ theme }) => { component={renderInput} label={translate("ra.auth.password")} type="password" - disabled={loading} + disabled={loading || !supportPassAuth} resettable fullWidth /> @@ -273,13 +343,24 @@ const LoginPage = ({ theme }) => { variant="contained" type="submit" color="primary" - disabled={loading} + disabled={loading || !supportPassAuth} className={classes.button} fullWidth > {loading && } {translate("ra.auth.sign_in")} + diff --git a/src/i18n/de.js b/src/i18n/de.js index ced6512..9688468 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -10,6 +10,7 @@ const de = { username_error: "Bitte vollständigen Nutzernamen angeben: '@user:domain'", protocol_error: "Die URL muss mit 'http://' oder 'https://' beginnen", url_error: "Keine gültige Matrix Server URL", + sso_sign_in: "Anmeldung mit SSO", }, users: { invalid_user_id: "Lokaler Anteil der Matrix Benutzer-ID ohne Homeserver.", diff --git a/src/i18n/en.js b/src/i18n/en.js index 44e6ed2..b1e379a 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -10,6 +10,7 @@ const en = { username_error: "Please enter fully qualified user ID: '@user:domain'", protocol_error: "URL has to start with 'http://' or 'https://'", url_error: "Not a valid Matrix server URL", + sso_sign_in: "Sign in with SSO", }, users: { invalid_user_id: "Localpart of a Matrix user-id without homeserver.", diff --git a/src/i18n/zh.js b/src/i18n/zh.js index 3c36366..7a9b4e7 100644 --- a/src/i18n/zh.js +++ b/src/i18n/zh.js @@ -10,10 +10,12 @@ const zh = { username_error: "请输入完整有效的用户 ID: '@user:domain'", protocol_error: "URL 需要以'http://'或'https://'作为起始", url_error: "不是一个有效的 Matrix 服务器地址", + sso_sign_in: "使用 SSO 登录", }, users: { invalid_user_id: "必须要是一个有效的 Matrix 用户 ID ,例如 @user_id:homeserver", + tabs: { sso: "SSO" }, }, rooms: { tabs: { diff --git a/src/synapse/authProvider.js b/src/synapse/authProvider.js index 470637f..723f155 100644 --- a/src/synapse/authProvider.js +++ b/src/synapse/authProvider.js @@ -2,20 +2,31 @@ import { fetchUtils } from "react-admin"; const authProvider = { // called when the user attempts to log in - login: ({ base_url, username, password }) => { + login: ({ base_url, username, password, loginToken }) => { // force homeserver for protection in case the form is manipulated base_url = process.env.REACT_APP_SERVER || base_url; console.log("login "); const options = { method: "POST", - body: JSON.stringify({ - type: "m.login.password", - user: username, - password: password, - device_id: localStorage.getItem("device_id"), - initial_device_display_name: "Synapse Admin", - }), + body: JSON.stringify( + Object.assign( + { + device_id: localStorage.getItem("device_id"), + initial_device_display_name: "Synapse Admin", + }, + loginToken + ? { + type: "m.login.token", + token: loginToken, + } + : { + type: "m.login.password", + user: username, + password: password, + } + ) + ), }; // use the base_url from login instead of the well_known entry from the