From f8045e428bad7a1dde1df56fab1e4a3a62ea021d Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 2 Mar 2020 14:59:54 +0000
Subject: [PATCH 1/2] riot-desktop open SSO in browser so user doesn't have to
auth twice
---
src/BasePlatform.js | 24 ++++++++++++
src/Login.js | 28 ++++---------
src/components/structures/auth/Login.js | 12 ++++--
src/components/structures/auth/SoftLogout.js | 31 ++-------------
src/components/views/elements/SSOButton.js | 41 ++++++++++++++++++++
src/i18n/strings/en_EN.json | 2 +-
6 files changed, 86 insertions(+), 52 deletions(-)
create mode 100644 src/components/views/elements/SSOButton.js
diff --git a/src/BasePlatform.js b/src/BasePlatform.js
index a935f4a767..809fe198d6 100644
--- a/src/BasePlatform.js
+++ b/src/BasePlatform.js
@@ -4,6 +4,7 @@
Copyright 2016 Aviral Dasgupta
Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
+Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -18,6 +19,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import {MatrixClient} from "matrix-js-sdk";
import dis from './dispatcher';
import BaseEventIndexManager from './indexing/BaseEventIndexManager';
@@ -164,4 +166,26 @@ export default class BasePlatform {
}
setLanguage(preferredLangs: string[]) {}
+
+ getSSOCallbackUrl(hsUrl: string, isUrl: string): URL {
+ const url = new URL(window.location.href);
+ // XXX: at this point, the fragment will always be #/login, which is no
+ // use to anyone. Ideally, we would get the intended fragment from
+ // MatrixChat.screenAfterLogin so that you could follow #/room links etc
+ // through an SSO login.
+ url.hash = "";
+ url.searchParams.set("homeserver", hsUrl);
+ url.searchParams.set("identityServer", isUrl);
+ return url;
+ }
+
+ /**
+ * Begin Single Sign On flows.
+ * @param mxClient the matrix client using which we should start the flow
+ * @param loginType the type of SSO it is, CAS/SSO.
+ */
+ startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") {
+ const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl());
+ window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO
+ }
}
diff --git a/src/Login.js b/src/Login.js
index d9ce8adaaa..1590e5ac28 100644
--- a/src/Login.js
+++ b/src/Login.js
@@ -3,6 +3,7 @@ Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -19,8 +20,6 @@ limitations under the License.
import Matrix from "matrix-js-sdk";
-import url from 'url';
-
export default class Login {
constructor(hsUrl, isUrl, fallbackHsUrl, opts) {
this._hsUrl = hsUrl;
@@ -29,6 +28,7 @@ export default class Login {
this._currentFlowIndex = 0;
this._flows = [];
this._defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
+ this._tempClient = null; // memoize
}
getHomeserverUrl() {
@@ -40,10 +40,12 @@ export default class Login {
}
setHomeserverUrl(hsUrl) {
+ this._tempClient = null; // clear memoization
this._hsUrl = hsUrl;
}
setIdentityServerUrl(isUrl) {
+ this._tempClient = null; // clear memoization
this._isUrl = isUrl;
}
@@ -52,8 +54,9 @@ export default class Login {
* requests.
* @returns {MatrixClient}
*/
- _createTemporaryClient() {
- return Matrix.createClient({
+ createTemporaryClient() {
+ if (this._tempClient) return this._tempClient; // use memoization
+ return this._tempClient = Matrix.createClient({
baseUrl: this._hsUrl,
idBaseUrl: this._isUrl,
});
@@ -61,7 +64,7 @@ export default class Login {
getFlows() {
const self = this;
- const client = this._createTemporaryClient();
+ const client = this.createTemporaryClient();
return client.loginFlows().then(function(result) {
self._flows = result.flows;
self._currentFlowIndex = 0;
@@ -139,21 +142,6 @@ export default class Login {
throw error;
});
}
-
- getSsoLoginUrl(loginType) {
- const client = this._createTemporaryClient();
- const parsedUrl = url.parse(window.location.href, true);
-
- // XXX: at this point, the fragment will always be #/login, which is no
- // use to anyone. Ideally, we would get the intended fragment from
- // MatrixChat.screenAfterLogin so that you could follow #/room links etc
- // through an SSO login.
- parsedUrl.hash = "";
-
- parsedUrl.query["homeserver"] = client.getHomeserverUrl();
- parsedUrl.query["identityServer"] = client.getIdentityServerUrl();
- return client.getSsoLoginUrl(url.format(parsedUrl), loginType);
- }
}
diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js
index 24e4726416..3813146b5e 100644
--- a/src/components/structures/auth/Login.js
+++ b/src/components/structures/auth/Login.js
@@ -27,6 +27,7 @@ import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import classNames from "classnames";
import AuthPage from "../../views/auth/AuthPage";
+import SSOButton from "../../views/elements/SSOButton";
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@@ -120,8 +121,8 @@ export default createReactClass({
'm.login.password': this._renderPasswordStep,
// CAS and SSO are the same thing, modulo the url we link to
- 'm.login.cas': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("cas")),
- 'm.login.sso': () => this._renderSsoStep(this._loginLogic.getSsoLoginUrl("sso")),
+ 'm.login.cas': () => this._renderSsoStep("cas"),
+ 'm.login.sso': () => this._renderSsoStep("sso"),
};
this._initLoginLogic();
@@ -585,7 +586,7 @@ export default createReactClass({
);
},
- _renderSsoStep: function(url) {
+ _renderSsoStep: function(loginType) {
const SignInToText = sdk.getComponent('views.auth.SignInToText');
let onEditServerDetailsClick = null;
@@ -606,7 +607,10 @@ export default createReactClass({
{introText}
-