From 3930f1a10b117e2f2d40f471456d70cabce3c785 Mon Sep 17 00:00:00 2001 From: Kerry Date: Fri, 30 Jun 2023 10:33:44 +1200 Subject: [PATCH] OIDC: extract success/failure handlers from token login function (#11154) * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * move try again responsibility --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- src/Lifecycle.ts | 80 +++++++++++++++--------- src/components/structures/MatrixChat.tsx | 2 + src/i18n/strings/en_EN.json | 2 +- 3 files changed, 53 insertions(+), 31 deletions(-) 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",