Skip to content

Commit

Permalink
OIDC: extract success/failure handlers from token login function (#11154
Browse files Browse the repository at this point in the history
)

* 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>
  • Loading branch information
Kerry and richvdh committed Jun 29, 2023
1 parent f62fe26 commit 3930f1a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 31 deletions.
80 changes: 50 additions & 30 deletions src/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -204,55 +205,74 @@ 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);
}

return sendLoginRequest(homeserver, identityServer, "m.login.token", {
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<boolean> => {
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<void> {
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<void> {
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> | void {
if (e.reason === InvalidStoreError.TOGGLED_LAZY_LOADING) {
return Promise.resolve()
Expand Down
2 changes: 2 additions & 0 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
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,
});
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 3930f1a

Please sign in to comment.