From 84d81ff09b683b47a551e88c7f8c502a5ba67cd1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 2 Jun 2020 16:26:07 +0100 Subject: [PATCH 1/2] Fix login loop where the sso flow returns to `#/login` due to fragmentAfterLogin going back to `#/login` and https://github.com/vector-im/riot-web/issues/11643 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 16 +++++++++++----- src/Lifecycle.js | 17 ++++++++++------- src/components/structures/MatrixChat.tsx | 5 ++++- src/components/structures/auth/SoftLogout.js | 9 +++++---- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index c5f58f7f0c..98c37a86ce 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -25,6 +25,9 @@ import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; import {Action} from "./dispatcher/actions"; import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; +export const HS_URL_LS_KEY = "mx_hs_url"; +export const IS_URL_LS_KEY = "mx_is_url"; + export enum UpdateCheckStatus { Checking = "CHECKING", Error = "ERROR", @@ -47,6 +50,7 @@ export default abstract class BasePlatform { constructor() { dis.register(this.onAction); + this.startUpdateCheck = this.startUpdateCheck.bind(this); } protected onAction = (payload: ActionPayload) => { @@ -217,11 +221,9 @@ export default abstract class BasePlatform { setLanguage(preferredLangs: string[]) {} - getSSOCallbackUrl(hsUrl: string, isUrl: string, fragmentAfterLogin: string): URL { + getSSOCallbackUrl(fragmentAfterLogin: string): URL { const url = new URL(window.location.href); url.hash = fragmentAfterLogin || ""; - url.searchParams.set("homeserver", hsUrl); - url.searchParams.set("identityServer", isUrl); return url; } @@ -232,8 +234,12 @@ export default abstract class BasePlatform { * @param {string} fragmentAfterLogin the hash to pass to the app during sso callback. */ startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { - const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl(), - fragmentAfterLogin); + // persist hs url and is url for when the user is returned to the app with the login token + localStorage.setItem(HS_URL_LS_KEY, mxClient.getHomeserverUrl()); + if (mxClient.getIdentityServerUrl()) { + localStorage.setItem(IS_URL_LS_KEY, mxClient.getIdentityServerUrl()); + } + const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 0494628472..58720682bb 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -41,6 +41,7 @@ import {IntegrationManagers} from "./integrations/IntegrationManagers"; import {Mjolnir} from "./mjolnir/Mjolnir"; import DeviceListener from "./DeviceListener"; import {Jitsi} from "./widgets/Jitsi"; +import {HS_URL_LS_KEY, IS_URL_LS_KEY} from "./BasePlatform"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -163,14 +164,16 @@ export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) { return Promise.resolve(false); } - if (!queryParams.homeserver) { + const homeserver = localStorage.getItem(HS_URL_LS_KEY); + const identityServer = localStorage.getItem(IS_URL_LS_KEY); + if (!homeserver) { console.warn("Cannot log in with token: can't determine HS URL to use"); return Promise.resolve(false); } return sendLoginRequest( - queryParams.homeserver, - queryParams.identityServer, + homeserver, + identityServer, "m.login.token", { token: queryParams.loginToken, initial_device_display_name: defaultDeviceDisplayName, @@ -256,8 +259,8 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { * @returns {Object} Information about the session - see implementation for variables. */ export function getLocalStorageSessionVars() { - const hsUrl = localStorage.getItem("mx_hs_url"); - const isUrl = localStorage.getItem("mx_is_url"); + const hsUrl = localStorage.getItem(HS_URL_LS_KEY); + const isUrl = localStorage.getItem(IS_URL_LS_KEY); const accessToken = localStorage.getItem("mx_access_token"); const userId = localStorage.getItem("mx_user_id"); const deviceId = localStorage.getItem("mx_device_id"); @@ -486,9 +489,9 @@ function _showStorageEvictedDialog() { class AbortLoginAndRebuildStorage extends Error { } function _persistCredentialsToLocalStorage(credentials) { - localStorage.setItem("mx_hs_url", credentials.homeserverUrl); + localStorage.setItem(HS_URL_LS_KEY, credentials.homeserverUrl); if (credentials.identityServerUrl) { - localStorage.setItem("mx_is_url", credentials.identityServerUrl); + localStorage.setItem(IS_URL_LS_KEY, credentials.identityServerUrl); } localStorage.setItem("mx_user_id", credentials.userId); localStorage.setItem("mx_access_token", credentials.accessToken); diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 058a7ba50b..9527d65eea 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1942,7 +1942,10 @@ export default class MatrixChat extends React.PureComponent { // console.log(`Rendering MatrixChat with view ${this.state.view}`); let fragmentAfterLogin = ""; - if (this.props.initialScreenAfterLogin) { + if (this.props.initialScreenAfterLogin && + // XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop + !["welcome", "login", "register"].includes(this.props.initialScreenAfterLogin.screen) + ) { fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`; } diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index 5d9f868f85..58a3fc3c15 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -25,6 +25,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {sendLoginRequest} from "../../../Login"; import AuthPage from "../../views/auth/AuthPage"; import SSOButton from "../../views/elements/SSOButton"; +import {HS_URL_LS_KEY, IS_URL_LS_KEY} from "../../../BasePlatform"; const LOGIN_VIEW = { LOADING: 1, @@ -43,7 +44,7 @@ const FLOWS_TO_VIEWS = { export default class SoftLogout extends React.Component { static propTypes = { // Query parameters from MatrixChat - realQueryParams: PropTypes.object, // {homeserver, identityServer, loginToken} + realQueryParams: PropTypes.object, // {loginToken} // Called when the SSO login completes onTokenLoginCompleted: PropTypes.func, @@ -90,7 +91,7 @@ export default class SoftLogout extends React.Component { async _initLogin() { const queryParams = this.props.realQueryParams; - const hasAllParams = queryParams && queryParams['homeserver'] && queryParams['loginToken']; + const hasAllParams = queryParams && queryParams['loginToken']; if (hasAllParams) { this.setState({loginView: LOGIN_VIEW.LOADING}); this.trySsoLogin(); @@ -157,8 +158,8 @@ export default class SoftLogout extends React.Component { async trySsoLogin() { this.setState({busy: true}); - const hsUrl = this.props.realQueryParams['homeserver']; - const isUrl = this.props.realQueryParams['identityServer'] || MatrixClientPeg.get().getIdentityServerUrl(); + const hsUrl = localStorage.getItem(HS_URL_LS_KEY); + const isUrl = localStorage.getItem(IS_URL_LS_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); const loginType = "m.login.token"; const loginParams = { token: this.props.realQueryParams['loginToken'], From 096045a037ab5a5f4b8d834bd85ab74dc1c8f08d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 3 Jun 2020 20:23:01 +0100 Subject: [PATCH 2/2] change names of consts for clarity Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.ts | 8 ++++---- src/Lifecycle.js | 14 +++++++------- src/components/structures/auth/SoftLogout.js | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts index 98c37a86ce..520c3fbe46 100644 --- a/src/BasePlatform.ts +++ b/src/BasePlatform.ts @@ -25,8 +25,8 @@ import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; import {Action} from "./dispatcher/actions"; import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; -export const HS_URL_LS_KEY = "mx_hs_url"; -export const IS_URL_LS_KEY = "mx_is_url"; +export const HOMESERVER_URL_KEY = "mx_hs_url"; +export const ID_SERVER_URL_KEY = "mx_is_url"; export enum UpdateCheckStatus { Checking = "CHECKING", @@ -235,9 +235,9 @@ export default abstract class BasePlatform { */ startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { // persist hs url and is url for when the user is returned to the app with the login token - localStorage.setItem(HS_URL_LS_KEY, mxClient.getHomeserverUrl()); + localStorage.setItem(HOMESERVER_URL_KEY, mxClient.getHomeserverUrl()); if (mxClient.getIdentityServerUrl()) { - localStorage.setItem(IS_URL_LS_KEY, mxClient.getIdentityServerUrl()); + localStorage.setItem(ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl()); } const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 58720682bb..d018ea99aa 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -41,7 +41,7 @@ import {IntegrationManagers} from "./integrations/IntegrationManagers"; import {Mjolnir} from "./mjolnir/Mjolnir"; import DeviceListener from "./DeviceListener"; import {Jitsi} from "./widgets/Jitsi"; -import {HS_URL_LS_KEY, IS_URL_LS_KEY} from "./BasePlatform"; +import {HOMESERVER_URL_KEY, ID_SERVER_URL_KEY} from "./BasePlatform"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -164,8 +164,8 @@ export function attemptTokenLogin(queryParams, defaultDeviceDisplayName) { return Promise.resolve(false); } - const homeserver = localStorage.getItem(HS_URL_LS_KEY); - const identityServer = localStorage.getItem(IS_URL_LS_KEY); + const homeserver = localStorage.getItem(HOMESERVER_URL_KEY); + const identityServer = localStorage.getItem(ID_SERVER_URL_KEY); if (!homeserver) { console.warn("Cannot log in with token: can't determine HS URL to use"); return Promise.resolve(false); @@ -259,8 +259,8 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { * @returns {Object} Information about the session - see implementation for variables. */ export function getLocalStorageSessionVars() { - const hsUrl = localStorage.getItem(HS_URL_LS_KEY); - const isUrl = localStorage.getItem(IS_URL_LS_KEY); + const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY); + const isUrl = localStorage.getItem(ID_SERVER_URL_KEY); const accessToken = localStorage.getItem("mx_access_token"); const userId = localStorage.getItem("mx_user_id"); const deviceId = localStorage.getItem("mx_device_id"); @@ -489,9 +489,9 @@ function _showStorageEvictedDialog() { class AbortLoginAndRebuildStorage extends Error { } function _persistCredentialsToLocalStorage(credentials) { - localStorage.setItem(HS_URL_LS_KEY, credentials.homeserverUrl); + localStorage.setItem(HOMESERVER_URL_KEY, credentials.homeserverUrl); if (credentials.identityServerUrl) { - localStorage.setItem(IS_URL_LS_KEY, credentials.identityServerUrl); + localStorage.setItem(ID_SERVER_URL_KEY, credentials.identityServerUrl); } localStorage.setItem("mx_user_id", credentials.userId); localStorage.setItem("mx_access_token", credentials.accessToken); diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index 58a3fc3c15..a2824b63a3 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -25,7 +25,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {sendLoginRequest} from "../../../Login"; import AuthPage from "../../views/auth/AuthPage"; import SSOButton from "../../views/elements/SSOButton"; -import {HS_URL_LS_KEY, IS_URL_LS_KEY} from "../../../BasePlatform"; +import {HOMESERVER_URL_KEY, ID_SERVER_URL_KEY} from "../../../BasePlatform"; const LOGIN_VIEW = { LOADING: 1, @@ -158,8 +158,8 @@ export default class SoftLogout extends React.Component { async trySsoLogin() { this.setState({busy: true}); - const hsUrl = localStorage.getItem(HS_URL_LS_KEY); - const isUrl = localStorage.getItem(IS_URL_LS_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); + const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY); + const isUrl = localStorage.getItem(ID_SERVER_URL_KEY) || MatrixClientPeg.get().getIdentityServerUrl(); const loginType = "m.login.token"; const loginParams = { token: this.props.realQueryParams['loginToken'],