diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index c3366cdc5c..2931e6c5ef 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -17,6 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { ReactNode } from "react"; import { createClient } from "matrix-js-sdk/src/matrix"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { MatrixClient } from "matrix-js-sdk/src/client"; @@ -204,14 +205,12 @@ export function attemptTokenLogin( const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY) ?? undefined; if (!homeserver) { logger.warn("Cannot log in with token: can't determine HS URL to use"); - Modal.createDialog(ErrorDialog, { - title: _t("We couldn't log you in"), - description: _t( + onFailedDelegatedAuthLogin( + _t( "We asked the browser to remember which homeserver you use to let you sign in, " + "but unfortunately your browser has forgotten it. Go to the sign in page and try again.", ), - button: _t("Try again"), - }); + ); return Promise.resolve(false); } @@ -219,40 +218,61 @@ export function attemptTokenLogin( token: queryParams.loginToken as string, initial_device_display_name: defaultDeviceDisplayName, }) - .then(function (creds) { + .then(async function (creds) { logger.log("Logged in with token"); - return clearStorage().then(async (): Promise => { - await persistCredentials(creds); - // remember that we just logged in - sessionStorage.setItem("mx_fresh_login", String(true)); - return true; - }); + await onSuccessfulDelegatedAuthLogin(creds); + return true; }) - .catch((err) => { - Modal.createDialog(ErrorDialog, { - title: _t("We couldn't log you in"), - description: messageForLoginError(err, { + .catch((error) => { + const tryAgainCallback: TryAgainFunction = () => { + const cli = createClient({ + baseUrl: homeserver, + idBaseUrl: identityServer, + }); + const idpId = localStorage.getItem(SSO_IDP_ID_KEY) || undefined; + PlatformPeg.get()?.startSingleSignOn(cli, "sso", fragmentAfterLogin, idpId, SSOAction.LOGIN); + }; + onFailedDelegatedAuthLogin( + messageForLoginError(error, { hsUrl: homeserver, hsName: homeserver, }), - button: _t("Try again"), - onFinished: (tryAgain) => { - if (tryAgain) { - const cli = createClient({ - baseUrl: homeserver, - idBaseUrl: identityServer, - }); - const idpId = localStorage.getItem(SSO_IDP_ID_KEY) || undefined; - PlatformPeg.get()?.startSingleSignOn(cli, "sso", fragmentAfterLogin, idpId, SSOAction.LOGIN); - } - }, - }); - logger.error("Failed to log in with login token:"); - logger.error(err); + tryAgainCallback, + ); + logger.error("Failed to log in with login token:", error); return false; }); } +/** + * Called after a successful token login or OIDC authorization. + * Clear storage then save new credentials in storage + * @param credentials as returned from login + */ +async function onSuccessfulDelegatedAuthLogin(credentials: IMatrixClientCreds): Promise { + await clearStorage(); + await persistCredentials(credentials); + + // remember that we just logged in + sessionStorage.setItem("mx_fresh_login", String(true)); +} + +type TryAgainFunction = () => void; +/** + * Display a friendly error to the user when token login or OIDC authorization fails + * @param description error description + * @param tryAgain OPTIONAL function to call on try again button from error dialog + */ +async function onFailedDelegatedAuthLogin(description: string | ReactNode, tryAgain?: TryAgainFunction): Promise { + Modal.createDialog(ErrorDialog, { + title: _t("We couldn't log you in"), + description, + button: _t("Try again"), + // if we have a tryAgain callback, call it the primary 'try again' button was clicked in the dialog + onFinished: tryAgain ? (shouldTryAgain?: boolean) => shouldTryAgain && tryAgain() : undefined, + }); +} + export function handleInvalidStoreError(e: InvalidStoreError): Promise | void { if (e.reason === InvalidStoreError.TOGGLED_LAZY_LOADING) { return Promise.resolve() diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 3383e8b039..b65e559ef0 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -330,6 +330,8 @@ export default class MatrixChat extends React.PureComponent { this.tokenLogin = true; // Create and start the client + // accesses the new credentials just set in storage during attemptTokenLogin + // and sets logged in state await Lifecycle.restoreFromLocalStorage({ ignoreGuest: true, }); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index dff93e43b9..34f12a1095 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -101,8 +101,8 @@ "Failed to transfer call": "Failed to transfer call", "Permission Required": "Permission Required", "You do not have permission to start a conference call in this room": "You do not have permission to start a conference call in this room", - "We couldn't log you in": "We couldn't log you in", "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.", + "We couldn't log you in": "We couldn't log you in", "Try again": "Try again", "User is not logged in": "User is not logged in", "Database unexpectedly closed": "Database unexpectedly closed",