Clean-up the fallback login code. (#7657)
parent
236d2d699d
commit
191dc98f80
|
@ -0,0 +1 @@
|
||||||
|
Clean-up the login fallback code.
|
|
@ -1,6 +1,7 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<title> Login </title>
|
<title> Login </title>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0'>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
@ -8,17 +9,16 @@
|
||||||
<script src="js/login.js"></script>
|
<script src="js/login.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="matrixLogin.onLoad()">
|
<body onload="matrixLogin.onLoad()">
|
||||||
<center>
|
<div id="container">
|
||||||
<br/>
|
|
||||||
<h1 id="title"></h1>
|
<h1 id="title"></h1>
|
||||||
|
|
||||||
<span id="feedback" style="color: #f00"></span>
|
<span id="feedback"></span>
|
||||||
|
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<img src="spinner.gif" />
|
<img src="spinner.gif" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="sso_flow" class="login_flow" style="display:none">
|
<div id="sso_flow" class="login_flow" style="display: none;">
|
||||||
Single-sign on:
|
Single-sign on:
|
||||||
<form id="sso_form" action="/_matrix/client/r0/login/sso/redirect" method="get">
|
<form id="sso_form" action="/_matrix/client/r0/login/sso/redirect" method="get">
|
||||||
<input id="sso_redirect_url" type="hidden" name="redirectUrl" value=""/>
|
<input id="sso_redirect_url" type="hidden" name="redirectUrl" value=""/>
|
||||||
|
@ -26,9 +26,9 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="password_flow" class="login_flow" style="display:none">
|
<div id="password_flow" class="login_flow" style="display: none;">
|
||||||
Password Authentication:
|
Password Authentication:
|
||||||
<form onsubmit="matrixLogin.password_login(); return false;">
|
<form onsubmit="matrixLogin.passwordLogin(); return false;">
|
||||||
<input id="user_id" size="32" type="text" placeholder="Matrix ID (e.g. bob)" autocapitalize="off" autocorrect="off" />
|
<input id="user_id" size="32" type="text" placeholder="Matrix ID (e.g. bob)" autocapitalize="off" autocorrect="off" />
|
||||||
<br/>
|
<br/>
|
||||||
<input id="password" size="32" type="password" placeholder="Password"/>
|
<input id="password" size="32" type="password" placeholder="Password"/>
|
||||||
|
@ -38,9 +38,9 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="no_login_types" type="button" class="login_flow" style="display:none">
|
<div id="no_login_types" type="button" class="login_flow" style="display: none;">
|
||||||
Log in currently unavailable.
|
Log in currently unavailable.
|
||||||
</div>
|
</div>
|
||||||
</center>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -5,11 +5,11 @@ window.matrixLogin = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Titles get updated through the process to give users feedback.
|
// Titles get updated through the process to give users feedback.
|
||||||
var TITLE_PRE_AUTH = "Log in with one of the following methods";
|
const TITLE_PRE_AUTH = "Log in with one of the following methods";
|
||||||
var TITLE_POST_AUTH = "Logging in...";
|
const TITLE_POST_AUTH = "Logging in...";
|
||||||
|
|
||||||
// The cookie used to store the original query parameters when using SSO.
|
// The cookie used to store the original query parameters when using SSO.
|
||||||
var COOKIE_KEY = "synapse_login_fallback_qs";
|
const COOKIE_KEY = "synapse_login_fallback_qs";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submit a login request.
|
* Submit a login request.
|
||||||
|
@ -20,9 +20,9 @@ var COOKIE_KEY = "synapse_login_fallback_qs";
|
||||||
* login request, e.g. device_id.
|
* login request, e.g. device_id.
|
||||||
* callback: (Optional) Function to call on successful login.
|
* callback: (Optional) Function to call on successful login.
|
||||||
*/
|
*/
|
||||||
var submitLogin = function(type, data, extra, callback) {
|
function submitLogin(type, data, extra, callback) {
|
||||||
console.log("Logging in with " + type);
|
console.log("Logging in with " + type);
|
||||||
set_title(TITLE_POST_AUTH);
|
setTitle(TITLE_POST_AUTH);
|
||||||
|
|
||||||
// Add the login type.
|
// Add the login type.
|
||||||
data.type = type;
|
data.type = type;
|
||||||
|
@ -41,12 +41,15 @@ var submitLogin = function(type, data, extra, callback) {
|
||||||
}
|
}
|
||||||
matrixLogin.onLogin(response);
|
matrixLogin.onLogin(response);
|
||||||
}).fail(errorFunc);
|
}).fail(errorFunc);
|
||||||
};
|
}
|
||||||
|
|
||||||
var errorFunc = function(err) {
|
/*
|
||||||
|
* Display an error to the user and show the login form again.
|
||||||
|
*/
|
||||||
|
function errorFunc(err) {
|
||||||
// We want to show the error to the user rather than redirecting immediately to the
|
// We want to show the error to the user rather than redirecting immediately to the
|
||||||
// SSO portal (if SSO is the only login option), so we inhibit the redirect.
|
// SSO portal (if SSO is the only login option), so we inhibit the redirect.
|
||||||
show_login(true);
|
showLogin(true);
|
||||||
|
|
||||||
if (err.responseJSON && err.responseJSON.error) {
|
if (err.responseJSON && err.responseJSON.error) {
|
||||||
setFeedbackString(err.responseJSON.error + " (" + err.responseJSON.errcode + ")");
|
setFeedbackString(err.responseJSON.error + " (" + err.responseJSON.errcode + ")");
|
||||||
|
@ -54,27 +57,42 @@ var errorFunc = function(err) {
|
||||||
else {
|
else {
|
||||||
setFeedbackString("Request failed: " + err.status);
|
setFeedbackString("Request failed: " + err.status);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
var setFeedbackString = function(text) {
|
/*
|
||||||
|
* Display an error to the user.
|
||||||
|
*/
|
||||||
|
function setFeedbackString(text) {
|
||||||
$("#feedback").text(text);
|
$("#feedback").text(text);
|
||||||
};
|
}
|
||||||
|
|
||||||
var show_login = function(inhibit_redirect) {
|
/*
|
||||||
// Set the redirect to come back to this page, a login token will get added
|
* (Maybe) Show the login forms.
|
||||||
// and handled after the redirect.
|
*
|
||||||
var this_page = window.location.origin + window.location.pathname;
|
* This actually does a few unrelated functions:
|
||||||
$("#sso_redirect_url").val(this_page);
|
*
|
||||||
|
* * Configures the SSO redirect URL to come back to this page.
|
||||||
|
* * Configures and shows the SSO form, if the server supports SSO.
|
||||||
|
* * Otherwise, shows the password form.
|
||||||
|
*/
|
||||||
|
function showLogin(inhibitRedirect) {
|
||||||
|
setTitle(TITLE_PRE_AUTH);
|
||||||
|
|
||||||
// If inhibit_redirect is false, and SSO is the only supported login method,
|
// If inhibitRedirect is false, and SSO is the only supported login method,
|
||||||
// we can redirect straight to the SSO page.
|
// we can redirect straight to the SSO page.
|
||||||
if (matrixLogin.serverAcceptsSso) {
|
if (matrixLogin.serverAcceptsSso) {
|
||||||
|
// Set the redirect to come back to this page, a login token will get
|
||||||
|
// added as a query parameter and handled after the redirect.
|
||||||
|
$("#sso_redirect_url").val(window.location.origin + window.location.pathname);
|
||||||
|
|
||||||
// Before submitting SSO, set the current query parameters into a cookie
|
// Before submitting SSO, set the current query parameters into a cookie
|
||||||
// for retrieval later.
|
// for retrieval later.
|
||||||
var qs = parseQsFromUrl();
|
var qs = parseQsFromUrl();
|
||||||
setCookie(COOKIE_KEY, JSON.stringify(qs));
|
setCookie(COOKIE_KEY, JSON.stringify(qs));
|
||||||
|
|
||||||
if (!inhibit_redirect && !matrixLogin.serverAcceptsPassword) {
|
// If password is not supported and redirects are allowed, then submit
|
||||||
|
// the form (redirecting to the SSO provider).
|
||||||
|
if (!inhibitRedirect && !matrixLogin.serverAcceptsPassword) {
|
||||||
$("#sso_form").submit();
|
$("#sso_form").submit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -87,29 +105,38 @@ var show_login = function(inhibit_redirect) {
|
||||||
$("#password_flow").show();
|
$("#password_flow").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If neither password or SSO are supported, show an error to the user.
|
||||||
if (!matrixLogin.serverAcceptsPassword && !matrixLogin.serverAcceptsSso) {
|
if (!matrixLogin.serverAcceptsPassword && !matrixLogin.serverAcceptsSso) {
|
||||||
$("#no_login_types").show();
|
$("#no_login_types").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
set_title(TITLE_PRE_AUTH);
|
|
||||||
|
|
||||||
$("#loading").hide();
|
$("#loading").hide();
|
||||||
};
|
}
|
||||||
|
|
||||||
var show_spinner = function() {
|
/*
|
||||||
|
* Hides the forms and shows a loading throbber.
|
||||||
|
*/
|
||||||
|
function showSpinner() {
|
||||||
$("#password_flow").hide();
|
$("#password_flow").hide();
|
||||||
$("#sso_flow").hide();
|
$("#sso_flow").hide();
|
||||||
$("#no_login_types").hide();
|
$("#no_login_types").hide();
|
||||||
$("#loading").show();
|
$("#loading").show();
|
||||||
};
|
}
|
||||||
|
|
||||||
var set_title = function(title) {
|
/*
|
||||||
|
* Helper to show the page's main title.
|
||||||
|
*/
|
||||||
|
function setTitle(title) {
|
||||||
$("#title").text(title);
|
$("#title").text(title);
|
||||||
};
|
}
|
||||||
|
|
||||||
var fetch_info = function(cb) {
|
/*
|
||||||
|
* Query the login endpoint for the homeserver's supported flows.
|
||||||
|
*
|
||||||
|
* This populates matrixLogin.serverAccepts* variables.
|
||||||
|
*/
|
||||||
|
function fetchLoginFlows(cb) {
|
||||||
$.get(matrixLogin.endpoint, function(response) {
|
$.get(matrixLogin.endpoint, function(response) {
|
||||||
var serverAcceptsPassword = false;
|
|
||||||
for (var i = 0; i < response.flows.length; i++) {
|
for (var i = 0; i < response.flows.length; i++) {
|
||||||
var flow = response.flows[i];
|
var flow = response.flows[i];
|
||||||
if ("m.login.sso" === flow.type) {
|
if ("m.login.sso" === flow.type) {
|
||||||
|
@ -126,27 +153,41 @@ var fetch_info = function(cb) {
|
||||||
}).fail(errorFunc);
|
}).fail(errorFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called on load to fetch login flows and attempt SSO login (if a token is available).
|
||||||
|
*/
|
||||||
matrixLogin.onLoad = function() {
|
matrixLogin.onLoad = function() {
|
||||||
fetch_info(function() {
|
fetchLoginFlows(function() {
|
||||||
if (!try_token()) {
|
// (Maybe) attempt logging in via SSO if a token is available.
|
||||||
show_login(false);
|
if (!tryTokenLogin()) {
|
||||||
|
showLogin(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
matrixLogin.password_login = function() {
|
/*
|
||||||
|
* Submit simple user & password login.
|
||||||
|
*/
|
||||||
|
matrixLogin.passwordLogin = function() {
|
||||||
var user = $("#user_id").val();
|
var user = $("#user_id").val();
|
||||||
var pwd = $("#password").val();
|
var pwd = $("#password").val();
|
||||||
|
|
||||||
setFeedbackString("");
|
setFeedbackString("");
|
||||||
|
|
||||||
show_spinner();
|
showSpinner();
|
||||||
submitLogin(
|
submitLogin(
|
||||||
"m.login.password",
|
"m.login.password",
|
||||||
{user: user, password: pwd},
|
{user: user, password: pwd},
|
||||||
parseQsFromUrl());
|
parseQsFromUrl());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The onLogin function gets called after a succesful login.
|
||||||
|
*
|
||||||
|
* It is expected that implementations override this to be notified when the
|
||||||
|
* login is complete. The response to the login call is provided as the single
|
||||||
|
* parameter.
|
||||||
|
*/
|
||||||
matrixLogin.onLogin = function(response) {
|
matrixLogin.onLogin = function(response) {
|
||||||
// clobber this function
|
// clobber this function
|
||||||
console.warn("onLogin - This function should be replaced to proceed.");
|
console.warn("onLogin - This function should be replaced to proceed.");
|
||||||
|
@ -155,7 +196,7 @@ matrixLogin.onLogin = function(response) {
|
||||||
/*
|
/*
|
||||||
* Process the query parameters from the current URL into an object.
|
* Process the query parameters from the current URL into an object.
|
||||||
*/
|
*/
|
||||||
var parseQsFromUrl = function() {
|
function parseQsFromUrl() {
|
||||||
var pos = window.location.href.indexOf("?");
|
var pos = window.location.href.indexOf("?");
|
||||||
if (pos == -1) {
|
if (pos == -1) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -174,12 +215,12 @@ var parseQsFromUrl = function() {
|
||||||
result[key] = val;
|
result[key] = val;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the cookies and return an object.
|
* Process the cookies and return an object.
|
||||||
*/
|
*/
|
||||||
var parseCookies = function() {
|
function parseCookies() {
|
||||||
var allCookies = document.cookie;
|
var allCookies = document.cookie;
|
||||||
var result = {};
|
var result = {};
|
||||||
allCookies.split(";").forEach(function(part) {
|
allCookies.split(";").forEach(function(part) {
|
||||||
|
@ -196,32 +237,32 @@ var parseCookies = function() {
|
||||||
result[key] = val;
|
result[key] = val;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set a cookie that is valid for 1 hour.
|
* Set a cookie that is valid for 1 hour.
|
||||||
*/
|
*/
|
||||||
var setCookie = function(key, value) {
|
function setCookie(key, value) {
|
||||||
// The maximum age is set in seconds.
|
// The maximum age is set in seconds.
|
||||||
var maxAge = 60 * 60;
|
var maxAge = 60 * 60;
|
||||||
// Set the cookie, this defaults to the current domain and path.
|
// Set the cookie, this defaults to the current domain and path.
|
||||||
document.cookie = key + "=" + encodeURIComponent(value) + ";max-age=" + maxAge + ";sameSite=lax";
|
document.cookie = key + "=" + encodeURIComponent(value) + ";max-age=" + maxAge + ";sameSite=lax";
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Removes a cookie by key.
|
* Removes a cookie by key.
|
||||||
*/
|
*/
|
||||||
var deleteCookie = function(key) {
|
function deleteCookie(key) {
|
||||||
// Delete a cookie by setting the expiration to 0. (Note that the value
|
// Delete a cookie by setting the expiration to 0. (Note that the value
|
||||||
// doesn't matter.)
|
// doesn't matter.)
|
||||||
document.cookie = key + "=deleted;expires=0";
|
document.cookie = key + "=deleted;expires=0";
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Submits the login token if one is found in the query parameters. Returns a
|
* Submits the login token if one is found in the query parameters. Returns a
|
||||||
* boolean of whether the login token was found or not.
|
* boolean of whether the login token was found or not.
|
||||||
*/
|
*/
|
||||||
var try_token = function() {
|
function tryTokenLogin() {
|
||||||
// Check if the login token is in the query parameters.
|
// Check if the login token is in the query parameters.
|
||||||
var qs = parseQsFromUrl();
|
var qs = parseQsFromUrl();
|
||||||
|
|
||||||
|
@ -233,18 +274,18 @@ var try_token = function() {
|
||||||
// Retrieve the original query parameters (from before the SSO redirect).
|
// Retrieve the original query parameters (from before the SSO redirect).
|
||||||
// They are stored as JSON in a cookie.
|
// They are stored as JSON in a cookie.
|
||||||
var cookies = parseCookies();
|
var cookies = parseCookies();
|
||||||
var original_query_params = JSON.parse(cookies[COOKIE_KEY] || "{}")
|
var originalQueryParams = JSON.parse(cookies[COOKIE_KEY] || "{}")
|
||||||
|
|
||||||
// If the login is successful, delete the cookie.
|
// If the login is successful, delete the cookie.
|
||||||
var callback = function() {
|
function callback() {
|
||||||
deleteCookie(COOKIE_KEY);
|
deleteCookie(COOKIE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitLogin(
|
submitLogin(
|
||||||
"m.login.token",
|
"m.login.token",
|
||||||
{token: loginToken},
|
{token: loginToken},
|
||||||
original_query_params,
|
originalQueryParams,
|
||||||
callback);
|
callback);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
|
@ -31,20 +31,44 @@ form {
|
||||||
margin: 10px 0 0 0;
|
margin: 10px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add some padding to the viewport.
|
||||||
|
*/
|
||||||
|
#container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Center all direct children of the main form.
|
||||||
|
*/
|
||||||
|
#container > * {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A wrapper around each login flow.
|
||||||
|
*/
|
||||||
.login_flow {
|
.login_flow {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|
||||||
-webkit-border-radius: 10px;
|
|
||||||
-moz-border-radius: 10px;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|
||||||
-webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
|
|
||||||
-moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
|
|
||||||
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
|
box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.15);
|
||||||
|
|
||||||
background-color: #f8f8f8;
|
background-color: #f8f8f8;
|
||||||
border: 1px #ccc solid;
|
border: 1px #ccc solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to show error content.
|
||||||
|
*/
|
||||||
|
#feedback {
|
||||||
|
/* Red text. */
|
||||||
|
color: #ff0000;
|
||||||
|
/* A little space to not overlap the box-shadow. */
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue