From 0d41ffcf0e1e52a10725366f12ec81cb44e07500 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Wed, 3 Jun 2020 14:17:30 -0400 Subject: [PATCH 1/4] Accept a device ID to the login fallback endpoint. --- changelog.d/7629.bugfix | 1 + synapse/static/client/login/js/login.js | 60 +++++++++++++------------ 2 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 changelog.d/7629.bugfix diff --git a/changelog.d/7629.bugfix b/changelog.d/7629.bugfix new file mode 100644 index 000000000000..0debd580c529 --- /dev/null +++ b/changelog.d/7629.bugfix @@ -0,0 +1 @@ +Pass device information through to the login endpoint when using the login fallback. diff --git a/synapse/static/client/login/js/login.js b/synapse/static/client/login/js/login.js index 611df36d3f50..63c2979336da 100644 --- a/synapse/static/client/login/js/login.js +++ b/synapse/static/client/login/js/login.js @@ -7,26 +7,19 @@ window.matrixLogin = { var title_pre_auth = "Log in with one of the following methods"; var title_post_auth = "Logging in..."; -var submitPassword = function(user, pwd) { - console.log("Logging in with password..."); +var submitLogin = function(type, data) { + console.log("Logging in with " + type); set_title(title_post_auth); - var data = { - type: "m.login.password", - user: user, - password: pwd, - }; - $.post(matrixLogin.endpoint, JSON.stringify(data), function(response) { - matrixLogin.onLogin(response); - }).fail(errorFunc); -}; -var submitToken = function(loginToken) { - console.log("Logging in with login token..."); - set_title(title_post_auth); - var data = { - type: "m.login.token", - token: loginToken - }; + // Add the login type. + data.type = type; + + // Add the device ID, if one was provided. + var qs = parseQsFromUrl(); + if (qs.device_id) { + data.device_id = qs.device_id; + } + $.post(matrixLogin.endpoint, JSON.stringify(data), function(response) { matrixLogin.onLogin(response); }).fail(errorFunc); @@ -50,8 +43,9 @@ var setFeedbackString = function(text) { }; var show_login = function(inhibit_redirect) { - var this_page = window.location.origin + window.location.pathname; - $("#sso_redirect_url").val(this_page); + // Set the redirect to come back to this page, a login token will get added + // and handled after the redirect. + $("#sso_redirect_url").val(window.location.href); // If inhibit_redirect is false, and SSO is the only supported login method, we can // redirect straight to the SSO page @@ -123,7 +117,7 @@ matrixLogin.password_login = function() { setFeedbackString(""); show_spinner(); - submitPassword(user, pwd); + submitLogin("m.login.password", {user: user, password: pwd}); }; matrixLogin.onLogin = function(response) { @@ -131,7 +125,16 @@ matrixLogin.onLogin = function(response) { console.warn("onLogin - This function should be replaced to proceed."); }; -var parseQsFromUrl = function(query) { +/* + * Process the query parameters from the current URL into an object. + */ +var parseQsFromUrl = function() { + var pos = window.location.href.indexOf("?"); + if (pos == -1) { + return {}; + } + var query = window.location.href.substr(pos + 1); + var result = {}; query.split("&").forEach(function(part) { var item = part.split("="); @@ -146,20 +149,19 @@ var parseQsFromUrl = function(query) { return result; }; +/* + * Submits the login token if one is found in the query parameters. Returns a + * boolean of whether the login token was found or not. + */ var try_token = function() { - var pos = window.location.href.indexOf("?"); - if (pos == -1) { - return false; - } - var qs = parseQsFromUrl(window.location.href.substr(pos+1)); + var qs = parseQsFromUrl(); var loginToken = qs.loginToken; - if (!loginToken) { return false; } - submitToken(loginToken); + submitLogin("m.login.token", {token: loginToken}); return true; }; From f5de2fa2d7ac0ff24f0612502dc2266dda03802c Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Thu, 4 Jun 2020 07:44:35 -0400 Subject: [PATCH 2/4] Also pass the device display name. --- synapse/static/client/login/js/login.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/synapse/static/client/login/js/login.js b/synapse/static/client/login/js/login.js index 63c2979336da..ff8e4809bc6f 100644 --- a/synapse/static/client/login/js/login.js +++ b/synapse/static/client/login/js/login.js @@ -14,11 +14,14 @@ var submitLogin = function(type, data) { // Add the login type. data.type = type; - // Add the device ID, if one was provided. + // Add the device information, if it was provided. var qs = parseQsFromUrl(); if (qs.device_id) { data.device_id = qs.device_id; } + if (qs.initial_device_display_name) { + data.initial_device_display_name = qs.initial_device_display_name; + } $.post(matrixLogin.endpoint, JSON.stringify(data), function(response) { matrixLogin.onLogin(response); From 3c9e512c55b8fcec4eecc738ae024885016814f4 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Fri, 5 Jun 2020 13:50:29 -0400 Subject: [PATCH 3/4] Store the query parameters in a cookie during the SSO flow. --- synapse/static/client/login/js/login.js | 112 ++++++++++++++++++++---- 1 file changed, 96 insertions(+), 16 deletions(-) diff --git a/synapse/static/client/login/js/login.js b/synapse/static/client/login/js/login.js index ff8e4809bc6f..67ce0f9a4a2a 100644 --- a/synapse/static/client/login/js/login.js +++ b/synapse/static/client/login/js/login.js @@ -4,26 +4,41 @@ window.matrixLogin = { serverAcceptsSso: false, }; -var title_pre_auth = "Log in with one of the following methods"; -var title_post_auth = "Logging in..."; +// Titles get updated through the process to give users feedback. +var TITLE_PRE_AUTH = "Log in with one of the following methods"; +var TITLE_POST_AUTH = "Logging in..."; -var submitLogin = function(type, data) { +// The cookie used to store the original query parameters when using SSO. +var COOKIE_KEY = "synapse_login_fallback_qs"; + +/* + * Submit a login request. + * + * type: The login type as a string (e.g. "m.login.foo"). + * data: An object of data specific to the login type. + * extra: (Optional) An object to search for extra information to send with the + * login request, e.g. device_id. + * callback: (Optional) Function to call on successful login. + */ +var submitLogin = function(type, data, extra, callback) { console.log("Logging in with " + type); - set_title(title_post_auth); + set_title(TITLE_POST_AUTH); // Add the login type. data.type = type; // Add the device information, if it was provided. - var qs = parseQsFromUrl(); - if (qs.device_id) { - data.device_id = qs.device_id; + if (extra.device_id) { + data.device_id = extra.device_id; } - if (qs.initial_device_display_name) { - data.initial_device_display_name = qs.initial_device_display_name; + if (extra.initial_device_display_name) { + data.initial_device_display_name = extra.initial_device_display_name; } $.post(matrixLogin.endpoint, JSON.stringify(data), function(response) { + if (callback) { + callback(); + } matrixLogin.onLogin(response); }).fail(errorFunc); }; @@ -48,11 +63,17 @@ var setFeedbackString = function(text) { var show_login = function(inhibit_redirect) { // Set the redirect to come back to this page, a login token will get added // and handled after the redirect. - $("#sso_redirect_url").val(window.location.href); + var this_page = window.location.origin + window.location.pathname; + $("#sso_redirect_url").val(this_page); - // If inhibit_redirect is false, and SSO is the only supported login method, we can - // redirect straight to the SSO page + // If inhibit_redirect is false, and SSO is the only supported login method, + // we can redirect straight to the SSO page. if (matrixLogin.serverAcceptsSso) { + // Before submitted SSO, set the current query parameters into a cookie + // for retrieval later. + var qs = parseQsFromUrl(); + setCookie(COOKIE_KEY, JSON.stringify(qs)); + if (!inhibit_redirect && !matrixLogin.serverAcceptsPassword) { $("#sso_form").submit(); return; @@ -70,7 +91,7 @@ var show_login = function(inhibit_redirect) { $("#no_login_types").show(); } - set_title(title_pre_auth); + set_title(TITLE_PRE_AUTH); $("#loading").hide(); }; @@ -120,7 +141,10 @@ matrixLogin.password_login = function() { setFeedbackString(""); show_spinner(); - submitLogin("m.login.password", {user: user, password: pwd}); + submitLogin( + "m.login.password", + {user: user, password: pwd}, + parseQsFromUrl()); }; matrixLogin.onLogin = function(response) { @@ -147,16 +171,58 @@ var parseQsFromUrl = function() { if (val) { val = decodeURIComponent(val); } - result[key] = val + result[key] = val; + }); + return result; +}; + +/* + * Process the cookies and return an object. + */ +var parseCookies = function() { + var allCookies = document.cookie; + var result = {}; + allCookies.split(";").forEach(function(part) { + var item = part.split("="); + // Cookies might have arbitrary whitespace between them. + var key = item[0].trim(); + // You can end up with a broken cookie that doesn't have an equals sign + // in it. Set to an empty value. + var val = (item[1] || "").trim(); + // Values might be URI encoded. + if (val) { + val = decodeURIComponent(val); + } + result[key] = val; }); return result; }; +/* + * Set a cookie that is valid for 1 hour. + */ +var setCookie = function(key, value) { + // The maximum age is set in seconds. + var maxAge = 60 * 60; + // Set the cookie, this defaults to the current domain and path. + document.cookie = key + "=" + encodeURIComponent(value) + ";max-age=" + maxAge + ";sameSite=lax"; +}; + +/* + * Removes a cookie by key. + */ +var deleteCookie = function(key) { + // Delete a cookie by setting the expiration to 0. (Note that the value + // doesn't matter.) + document.cookie = key + "=deleted;expires=0"; +}; + /* * Submits the login token if one is found in the query parameters. Returns a * boolean of whether the login token was found or not. */ var try_token = function() { + // Check if the login token is in the query parameters. var qs = parseQsFromUrl(); var loginToken = qs.loginToken; @@ -164,7 +230,21 @@ var try_token = function() { return false; } - submitLogin("m.login.token", {token: loginToken}); + // Retrieve the original query parameters (from before the SSO redirect). + // They are stored as JSON in a cookie. + var cookies = parseCookies(); + var original_query_params = JSON.parse(cookies[COOKIE_KEY] || "{}") + + // If the login is successful, delete the cookie. + var callback = function() { + deleteCookie(COOKIE_KEY); + } + + submitLogin( + "m.login.token", + {token: loginToken}, + original_query_params, + callback); return true; }; From 12f3b7c3bd72381754a5314cb97fc33a3305e7e5 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Mon, 8 Jun 2020 09:21:39 -0400 Subject: [PATCH 4/4] Fix tense in comment. Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- synapse/static/client/login/js/login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/static/client/login/js/login.js b/synapse/static/client/login/js/login.js index 67ce0f9a4a2a..ba8048b23fad 100644 --- a/synapse/static/client/login/js/login.js +++ b/synapse/static/client/login/js/login.js @@ -69,7 +69,7 @@ var show_login = function(inhibit_redirect) { // If inhibit_redirect is false, and SSO is the only supported login method, // we can redirect straight to the SSO page. if (matrixLogin.serverAcceptsSso) { - // Before submitted SSO, set the current query parameters into a cookie + // Before submitting SSO, set the current query parameters into a cookie // for retrieval later. var qs = parseQsFromUrl(); setCookie(COOKIE_KEY, JSON.stringify(qs));