element-web/src/Lifecycle.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1169 lines
44 KiB
TypeScript
Raw Normal View History

/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
Copyright 2019, 2020, 2023 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ReactNode } from "react";
import { createClient, MatrixClient, SSOAction, OidcTokenRefresher, decodeBase64 } from "matrix-js-sdk/src/matrix";
import { IEncryptedPayload } from "matrix-js-sdk/src/crypto/aes";
import { QueryDict } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { IMatrixClientCreds, MatrixClientPeg, MatrixClientPegAssignOpts } from "./MatrixClientPeg";
Replace `SecurityCustomisations` with `CryptoSetupExtension` (#12342) * Changed call sites from customisations/security to ModuleRunner.extensions * Updated depenndecy and added tests * Fixed style and formatting with prettier * Fix according to Element PR comments * Fixing issues raised in PR review * Removed commented code. Improved encapsulation. Removed noisy logging * Improved language of comment about calling the factory * Refactor to get better encapsulation * Find a better name. Provide explicit reset function. Provide more TSDoc * Simplify mock for cryptoSetup, and add assertion for exception message. * Remove unused className property. Adjust TSDoc comments * Fix linting and code style issues * Added test to ensure we canregister anduse experimental extensions * Fix linting and code-style issues * Added test to ensure only on registration of experimental extensions * Added test toensure call to getDehydratedDeviceCallback() * Test what happens when there is no implementation * Iterating cryptoSetup tests * Lint/prettier fix * Assert both branches when checking for dehydrationkey callback * Update src/modules/ModuleRunner.ts Language and formatting Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Reset by setting a fresh ExtensionsManager Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Use regular comment instead of TSDoc style comment Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update test/MatrixClientPeg-test.ts No need to extend the base class Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix spelling Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix spelling Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix TSDoc formatting Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Simplify mock setup * Simplified mock and cleaned up a bit * Keeping track of extensions is an implementation detail internal to ExtensionsManager. Language and punctuation * Addressed issues and comments from PR review * Update src/modules/ModuleRunner.ts Keep the flags to track implementations as direct properties Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Fix flattening of implementation map * Update src/modules/ModuleRunner.ts Fix whitespace Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-04-12 15:15:17 +00:00
import { ModuleRunner } from "./modules/ModuleRunner";
import EventIndexPeg from "./indexing/EventIndexPeg";
import createMatrixClient from "./utils/createMatrixClient";
import Notifier from "./Notifier";
import UserActivity from "./UserActivity";
import Presence from "./Presence";
import dis from "./dispatcher/dispatcher";
import DMRoomMap from "./utils/DMRoomMap";
import Modal from "./Modal";
import ActiveWidgetStore from "./stores/ActiveWidgetStore";
import PlatformPeg from "./PlatformPeg";
import { sendLoginRequest } from "./Login";
import * as StorageManager from "./utils/StorageManager";
import * as StorageAccess from "./utils/StorageAccess";
import SettingsStore from "./settings/SettingsStore";
import { SettingLevel } from "./settings/SettingLevel";
import ToastStore from "./stores/ToastStore";
2021-06-29 12:11:58 +00:00
import { IntegrationManagers } from "./integrations/IntegrationManagers";
import { Mjolnir } from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener";
2021-06-29 12:11:58 +00:00
import { Jitsi } from "./widgets/Jitsi";
import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY, SSO_IDP_ID_KEY } from "./BasePlatform";
import ThreepidInviteStore from "./stores/ThreepidInviteStore";
import { PosthogAnalytics } from "./PosthogAnalytics";
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
import LegacyCallHandler from "./LegacyCallHandler";
import LifecycleCustomisations from "./customisations/Lifecycle";
2021-02-01 16:25:50 +00:00
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
2021-06-29 12:11:58 +00:00
import { _t } from "./languageHandler";
import SessionRestoreErrorDialog from "./components/views/dialogs/SessionRestoreErrorDialog";
import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog";
import { setSentryUser } from "./sentry";
import SdkConfig from "./SdkConfig";
import { DialogOpener } from "./utils/DialogOpener";
import { Action } from "./dispatcher/actions";
import AbstractLocalStorageSettingsHandler from "./settings/handlers/AbstractLocalStorageSettingsHandler";
import { OverwriteLoginPayload } from "./dispatcher/payloads/OverwriteLoginPayload";
import { SdkContextClass } from "./contexts/SDKContext";
2023-04-27 08:05:31 +00:00
import { messageForLoginError } from "./utils/ErrorUtils";
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
import { completeOidcLogin } from "./utils/oidc/authorize";
OIDC: add friendly errors (#11184) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * add friendly error messages for oidc authorization failures * i18n * update for new translations, tidy --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-19 02:46:37 +00:00
import { getOidcErrorMessage } from "./utils/oidc/error";
OIDC: revoke tokens on logout (#11718) * test persistCredentials without a pickle key * test setLoggedIn with pickle key * lint * type error * extract token persisting code into function, persist refresh token * store has_refresh_token too * pass refreshToken from oidcAuthGrant into credentials * rest restore session with pickle key * retreive stored refresh token and add to credentials * extract token decryption into function * remove TODO * very messy poc * utils to persist clientId and issuer after oidc authentication * add dep oidc-client-ts * persist issuer and clientId after successful oidc auth * add OidcClientStore * comments and tidy * expose getters for stored refresh and access tokens in Lifecycle * revoke tokens with oidc provider * test logout action in MatrixChat * comments * prettier * test OidcClientStore.revokeTokens * put pickle key destruction back * comment pedantry * working refresh without persistence * extract token persistence functions to utils * add sugar * implement TokenRefresher class with persistence * tidying * persist idTokenClaims * persist idTokenClaims * tests * remove unused cde * create token refresher during doSetLoggedIn * tidying * also tidying * OidcClientStore.initClient use stored issuer when client well known unavailable * test Lifecycle.logout * update Lifecycle test replaceUsingCreds calls * fix test * tidy * test tokenrefresher creation in login flow * test token refresher * Update src/utils/oidc/TokenRefresher.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * use literal value for m.authentication Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * improve comments * fix test mock, comment * typo * add sdkContext to SoftLogout, pass oidcClientStore to logout * fullstops * comments * fussy comment formatting --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-15 21:35:25 +00:00
import { OidcClientStore } from "./stores/oidc/OidcClientStore";
import {
getStoredOidcClientId,
getStoredOidcIdTokenClaims,
getStoredOidcTokenIssuer,
persistOidcAuthenticatedSettings,
} from "./utils/oidc/persistOidcSettings";
import {
ACCESS_TOKEN_IV,
ACCESS_TOKEN_STORAGE_KEY,
HAS_ACCESS_TOKEN_STORAGE_KEY,
HAS_REFRESH_TOKEN_STORAGE_KEY,
persistAccessTokenInStorage,
persistRefreshTokenInStorage,
REFRESH_TOKEN_IV,
REFRESH_TOKEN_STORAGE_KEY,
tryDecryptToken,
} from "./utils/tokens/tokens";
import { TokenRefresher } from "./utils/oidc/TokenRefresher";
const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url";
dis.register((payload) => {
if (payload.action === Action.TriggerLogout) {
// noinspection JSIgnoredPromiseFromCall - we don't care if it fails
onLoggedOut();
} else if (payload.action === Action.OverwriteLogin) {
const typed = <OverwriteLoginPayload>payload;
// Stop the current client before overwriting the login.
// If not done it might be impossible to clear the storage, as the
// rust crypto backend might be holding an open connection to the indexeddb store.
// We also use the `unsetClient` flag to false, because at this point we are
// already in the logged in flows of the `MatrixChat` component, and it will
// always expect to have a client (calls to `MatrixClientPeg.safeGet()`).
// If we unset the client and the component is updated, the render will fail and unmount everything.
// (The module dialog closes and fires a `aria_unhide_main_app` that will trigger a re-render)
stopMatrixClient(false);
doSetLoggedIn(typed.credentials, true).catch((e) => {
// XXX we might want to fire a new event here to let the app know that the login failed ?
// The module api could use it to display a message to the user.
logger.warn("Failed to overwrite login", e);
});
}
});
/**
* This is set to true by {@link #onSessionLockStolen}.
*
* It is used in various of the async functions to prevent races where we initialise a client after the lock is stolen.
*/
let sessionLockStolen = false;
// this is exposed solely for unit tests.
// ts-prune-ignore-next
export function setSessionLockNotStolen(): void {
sessionLockStolen = false;
}
/**
* Handle the session lock being stolen. Stops any active Matrix Client, and aborts any ongoing client initialisation.
*/
export async function onSessionLockStolen(): Promise<void> {
sessionLockStolen = true;
stopMatrixClient();
}
/**
* Check if we still hold the session lock.
*
* If not, raises a {@link SessionLockStolenError}.
*/
function checkSessionLock(): void {
if (sessionLockStolen) {
throw new SessionLockStolenError("session lock has been released");
}
}
/** Error type raised by various functions in the Lifecycle workflow if session lock is stolen during execution */
class SessionLockStolenError extends Error {}
2020-10-07 11:14:36 +00:00
interface ILoadSessionOpts {
enableGuest?: boolean;
guestHsUrl?: string;
guestIsUrl?: string;
ignoreGuest?: boolean;
defaultDeviceDisplayName?: string;
fragmentQueryParams?: QueryDict;
2020-10-07 11:14:36 +00:00
}
/**
* Called at startup, to attempt to build a logged-in Matrix session. It tries
* a number of things:
*
* 1. if we have a guest access token in the fragment query params, it uses
* that.
* 2. if an access token is stored in local storage (from a previous session),
* it uses that.
* 3. it attempts to auto-register as a guest user.
*
* If any of steps 1-4 are successful, it will call {_doSetLoggedIn}, which in
* turn will raise on_logged_in and will_start_client events.
*
2020-10-07 11:14:36 +00:00
* @param {object} [opts]
* @param {object} [opts.fragmentQueryParams]: string->string map of the
* query-parameters extracted from the #-fragment of the starting URI.
2020-10-07 11:14:36 +00:00
* @param {boolean} [opts.enableGuest]: set to true to enable guest access
* tokens and auto-guest registrations.
* @param {string} [opts.guestHsUrl]: homeserver URL. Only used if enableGuest
* is true; defines the HS to register against.
* @param {string} [opts.guestIsUrl]: homeserver URL. Only used if enableGuest
* is true; defines the IS to use.
* @param {bool} [opts.ignoreGuest]: If the stored session is a guest account,
* ignore it and don't load it.
* @param {string} [opts.defaultDeviceDisplayName]: Default display name to use
* when registering as a guest.
2017-05-04 17:03:35 +00:00
* @returns {Promise} a promise which resolves when the above process completes.
* Resolves to `true` if we ended up starting a session, or `false` if we
* failed.
*/
2020-10-07 11:14:36 +00:00
export async function loadSession(opts: ILoadSessionOpts = {}): Promise<boolean> {
2018-04-27 16:49:53 +00:00
try {
let enableGuest = opts.enableGuest || false;
const guestHsUrl = opts.guestHsUrl;
const guestIsUrl = opts.guestIsUrl;
const fragmentQueryParams = opts.fragmentQueryParams || {};
const defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
2018-04-27 13:20:09 +00:00
if (enableGuest && !guestHsUrl) {
logger.warn("Cannot enable guest access: can't determine HS URL to use");
enableGuest = false;
}
if (enableGuest && guestHsUrl && fragmentQueryParams.guest_user_id && fragmentQueryParams.guest_access_token) {
logger.log("Using guest access credentials");
2020-10-07 12:32:30 +00:00
return doSetLoggedIn(
{
userId: fragmentQueryParams.guest_user_id as string,
accessToken: fragmentQueryParams.guest_access_token as string,
homeserverUrl: guestHsUrl,
identityServerUrl: guestIsUrl,
guest: true,
},
true,
).then(() => true);
}
2020-10-07 12:32:30 +00:00
const success = await restoreFromLocalStorage({
2019-08-02 15:44:49 +00:00
ignoreGuest: Boolean(opts.ignoreGuest),
});
if (success) {
return true;
}
if (sessionLockStolen) {
return false;
}
if (enableGuest && guestHsUrl) {
2020-10-07 12:32:30 +00:00
return registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName);
}
// fall back to welcome screen
return false;
2018-04-27 16:49:53 +00:00
} catch (e) {
if (e instanceof AbortLoginAndRebuildStorage) {
// If we're aborting login because of a storage inconsistency, we don't
// need to show the general failure dialog. Instead, just go back to welcome.
return false;
}
// likewise, if the session lock has been stolen while we've been trying to start
if (sessionLockStolen) {
return false;
}
2020-10-07 12:32:30 +00:00
return handleLoadSessionFailure(e);
2018-04-27 16:49:53 +00:00
}
}
/**
* Gets the user ID of the persisted session, if one exists. This does not validate
* that the user's credentials still work, just that they exist and that a user ID
* is associated with them. The session is not loaded.
* @returns {[string, boolean]} The persisted session's owner and whether the stored
* session is for a guest user, if an owner exists. If there is no stored session,
* return [null, null].
*/
export async function getStoredSessionOwner(): Promise<[string, boolean] | [null, null]> {
2021-06-29 12:11:58 +00:00
const { hsUrl, userId, hasAccessToken, isGuest } = await getStoredSessionVars();
return hsUrl && userId && hasAccessToken ? [userId, !!isGuest] : [null, null];
}
/**
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
* If query string includes OIDC authorization code flow parameters attempt to login using oidc flow
* Else, we may be returning from SSO - attempt token login
*
* @param {Object} queryParams string->string map of the
* query-parameters extracted from the real query-string of the starting
* URI.
*
2021-02-01 16:25:50 +00:00
* @param {string} defaultDeviceDisplayName
* @param {string} fragmentAfterLogin path to go to after a successful login, only used for "Try again"
*
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
* @returns {Promise} promise which resolves to true if we completed the delegated auth login
* else false
*/
export async function attemptDelegatedAuthLogin(
queryParams: QueryDict,
defaultDeviceDisplayName?: string,
fragmentAfterLogin?: string,
): Promise<boolean> {
if (queryParams.code && queryParams.state) {
console.log("We have OIDC params - attempting OIDC login");
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
return attemptOidcNativeLogin(queryParams);
}
return attemptTokenLogin(queryParams, defaultDeviceDisplayName, fragmentAfterLogin);
}
/**
* Attempt to login by completing OIDC authorization code flow
* @param queryParams string->string map of the query-parameters extracted from the real query-string of the starting URI.
* @returns Promise that resolves to true when login succceeded, else false
*/
async function attemptOidcNativeLogin(queryParams: QueryDict): Promise<boolean> {
try {
const { accessToken, refreshToken, homeserverUrl, identityServerUrl, idToken, clientId, issuer } =
await completeOidcLogin(queryParams);
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
const {
user_id: userId,
device_id: deviceId,
is_guest: isGuest,
} = await getUserIdFromAccessToken(accessToken, homeserverUrl, identityServerUrl);
const credentials = {
accessToken,
refreshToken,
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
homeserverUrl,
identityServerUrl,
deviceId,
userId,
isGuest,
};
logger.debug("Logged in via OIDC native flow");
await onSuccessfulDelegatedAuthLogin(credentials);
// this needs to happen after success handler which clears storages
persistOidcAuthenticatedSettings(clientId, issuer, idToken);
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
return true;
} catch (error) {
logger.error("Failed to login via OIDC", error);
OIDC: add friendly errors (#11184) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * add friendly error messages for oidc authorization failures * i18n * update for new translations, tidy --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-19 02:46:37 +00:00
await onFailedDelegatedAuthLogin(getOidcErrorMessage(error as Error));
OIDC: Log in (#11199) * add delegatedauthentication to validated server config * dynamic client registration functions * test OP registration functions * add stubbed nativeOidc flow setup in Login * cover more error cases in Login * tidy * test dynamic client registration in Login * comment oidc_static_clients * register oidc inside Login.getFlows * strict fixes * remove unused code * and imports * comments * comments 2 * util functions to get static client id * check static client ids in login flow * remove dead code * OidcRegistrationClientMetadata type * navigate to oidc authorize url * exchange code for token * navigate to oidc authorize url * navigate to oidc authorize url * test * adjust for js-sdk code * login with oidc native flow: messy version * tidy * update test for response_mode query * tidy up some TODOs * use new types * add identityServerUrl to stored params * unit test completeOidcLogin * test tokenlogin * strict * whitespace * tidy * unit test oidc login flow in MatrixChat * strict * tidy * extract success/failure handlers from token login function * typo * use for no homeserver error dialog too * reuse post-token login functions, test * shuffle testing utils around * shuffle testing utils around * i18n * tidy * Update src/Lifecycle.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * tidy * comment * update tests for id token validation * move try again responsibility * prettier * use more future proof config for static clients * test util for oidcclientconfigs * rename type and lint * correct oidc test util * store issuer and clientId pre auth navigation * adjust for js-sdk changes * update for js-sdk userstate, tidy * update MatrixChat tests * update tests --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-07-11 04:09:18 +00:00
return false;
}
}
/**
* Gets information about the owner of a given access token.
* @param accessToken
* @param homeserverUrl
* @param identityServerUrl
* @returns Promise that resolves with whoami response
* @throws when whoami request fails
*/
async function getUserIdFromAccessToken(
accessToken: string,
homeserverUrl: string,
identityServerUrl?: string,
): Promise<ReturnType<MatrixClient["whoami"]>> {
try {
const client = createClient({
baseUrl: homeserverUrl,
accessToken: accessToken,
idBaseUrl: identityServerUrl,
});
return await client.whoami();
} catch (error) {
logger.error("Failed to retrieve userId using accessToken", error);
throw new Error("Failed to retrieve userId using accessToken");
}
}
/**
* @param {QueryDict} queryParams string->string map of the
* query-parameters extracted from the real query-string of the starting
* URI.
*
* @param {string} defaultDeviceDisplayName
* @param {string} fragmentAfterLogin path to go to after a successful login, only used for "Try again"
*
* @returns {Promise} promise which resolves to true if we completed the token
* login, else false
*/
2020-10-07 11:14:36 +00:00
export function attemptTokenLogin(
queryParams: QueryDict,
2020-10-07 11:14:36 +00:00
defaultDeviceDisplayName?: string,
2021-02-01 16:25:50 +00:00
fragmentAfterLogin?: string,
2020-10-07 11:14:36 +00:00
): Promise<boolean> {
if (!queryParams.loginToken) {
return Promise.resolve(false);
}
console.log("We have token login params - attempting token login");
const homeserver = localStorage.getItem(SSO_HOMESERVER_URL_KEY);
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");
onFailedDelegatedAuthLogin(_t("auth|sso_failed_missing_storage"));
return Promise.resolve(false);
}
return sendLoginRequest(homeserver, identityServer, "m.login.token", {
token: queryParams.loginToken as string,
initial_device_display_name: defaultDeviceDisplayName,
})
.then(async function (creds) {
logger.log("Logged in with token");
await onSuccessfulDelegatedAuthLogin(creds);
return true;
})
.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, {
2023-04-27 08:05:31 +00:00
hsUrl: homeserver,
hsName: homeserver,
}),
tryAgainCallback,
);
logger.error("Failed to log in with login token:", error);
return false;
2021-02-01 16:25:50 +00:00
});
}
/**
* 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("auth|oidc|error_title"),
description,
button: _t("action|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,
});
}
function registerAsGuest(hsUrl: string, isUrl?: string, defaultDeviceDisplayName?: string): Promise<boolean> {
logger.log(`Doing guest login on ${hsUrl}`);
// create a temporary MatrixClient to do the login
const client = createClient({
baseUrl: hsUrl,
});
return client
.registerGuest({
body: {
initial_device_display_name: defaultDeviceDisplayName,
},
})
.then(
(creds) => {
logger.log(`Registered as guest: ${creds.user_id}`);
2020-10-07 12:32:30 +00:00
return doSetLoggedIn(
{
userId: creds.user_id,
2016-08-11 13:21:52 +00:00
deviceId: creds.device_id,
accessToken: creds.access_token!,
homeserverUrl: hsUrl,
identityServerUrl: isUrl,
guest: true,
},
true,
).then(() => true);
},
(err) => {
logger.error("Failed to register as guest", err);
return false;
},
);
}
export interface IStoredSession {
2020-10-07 11:14:36 +00:00
hsUrl: string;
isUrl: string;
hasAccessToken: boolean;
2021-06-24 18:20:02 +00:00
accessToken: string | IEncryptedPayload;
hasRefreshToken: boolean;
refreshToken?: string | IEncryptedPayload;
2020-10-07 11:14:36 +00:00
userId: string;
deviceId: string;
isGuest: boolean;
}
/**
* Retrieve a token, as stored by `persistCredentials`
* Attempts to migrate token from localStorage to idb
* @param storageKey key used to store the token, eg ACCESS_TOKEN_STORAGE_KEY
* @returns Promise that resolves to token or undefined
*/
async function getStoredToken(storageKey: string): Promise<string | undefined> {
let token: string | undefined;
try {
token = await StorageAccess.idbLoad("account", storageKey);
} catch (e) {
logger.error(`StorageManager.idbLoad failed for account:${storageKey}`, e);
}
if (!token) {
token = localStorage.getItem(storageKey) ?? undefined;
if (token) {
try {
// try to migrate access token to IndexedDB if we can
await StorageAccess.idbSave("account", storageKey, token);
localStorage.removeItem(storageKey);
} catch (e) {
logger.error(`migration of token ${storageKey} to IndexedDB failed`, e);
}
}
}
return token;
}
/**
* Retrieves information about the stored session from the browser's storage. The session
* may not be valid, as it is not tested for consistency here.
* @returns {Object} Information about the session - see implementation for variables.
*/
export async function getStoredSessionVars(): Promise<Partial<IStoredSession>> {
const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY) ?? undefined;
const isUrl = localStorage.getItem(ID_SERVER_URL_KEY) ?? undefined;
const accessToken = await getStoredToken(ACCESS_TOKEN_STORAGE_KEY);
const refreshToken = await getStoredToken(REFRESH_TOKEN_STORAGE_KEY);
// if we pre-date storing "mx_has_access_token", but we retrieved an access
// token, then we should say we have an access token
const hasAccessToken = localStorage.getItem(HAS_ACCESS_TOKEN_STORAGE_KEY) === "true" || !!accessToken;
const hasRefreshToken = localStorage.getItem(HAS_REFRESH_TOKEN_STORAGE_KEY) === "true" || !!refreshToken;
const userId = localStorage.getItem("mx_user_id") ?? undefined;
const deviceId = localStorage.getItem("mx_device_id") ?? undefined;
let isGuest: boolean;
if (localStorage.getItem("mx_is_guest") !== null) {
isGuest = localStorage.getItem("mx_is_guest") === "true";
} else {
// legacy key name
isGuest = localStorage.getItem("matrix-is-guest") === "true";
}
return { hsUrl, isUrl, hasAccessToken, accessToken, refreshToken, hasRefreshToken, userId, deviceId, isGuest };
}
async function abortLogin(): Promise<void> {
const signOut = await showStorageEvictedDialog();
if (signOut) {
await clearStorage();
// This error feels a bit clunky, but we want to make sure we don't go any
// further and instead head back to sign in.
throw new AbortLoginAndRebuildStorage("Aborting login in progress because of storage inconsistency");
}
}
// returns a promise which resolves to true if a session is found in
// localstorage
2017-05-16 10:58:37 +00:00
//
// N.B. Lifecycle.js should not maintain any further localStorage state, we
// are moving towards using SessionStore to keep track of state related
// to the current session (which is typically backed by localStorage).
//
// The plan is to gradually move the localStorage access done here into
// SessionStore to avoid bugs where the view becomes out-of-sync with
2019-01-25 22:10:54 +00:00
// localStorage (e.g. isGuest etc.)
export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
2020-10-07 11:14:36 +00:00
const ignoreGuest = opts?.ignoreGuest;
2019-08-02 15:44:49 +00:00
2018-04-27 16:49:53 +00:00
if (!localStorage) {
return false;
}
const { hsUrl, isUrl, hasAccessToken, accessToken, refreshToken, userId, deviceId, isGuest } =
await getStoredSessionVars();
if (hasAccessToken && !accessToken) {
await abortLogin();
}
2018-04-27 16:49:53 +00:00
if (accessToken && userId && hsUrl) {
if (ignoreGuest && isGuest) {
logger.log("Ignoring stored guest account: " + userId);
return false;
}
const pickleKey = (await PlatformPeg.get()?.getPickleKey(userId, deviceId ?? "")) ?? undefined;
if (pickleKey) {
logger.log("Got pickle key");
} else {
logger.log("No pickle key available");
}
const decryptedAccessToken = await tryDecryptToken(pickleKey, accessToken, ACCESS_TOKEN_IV);
const decryptedRefreshToken = await tryDecryptToken(pickleKey, refreshToken, REFRESH_TOKEN_IV);
2020-10-07 11:14:36 +00:00
const freshLogin = sessionStorage.getItem("mx_fresh_login") === "true";
2020-09-30 04:52:47 +00:00
sessionStorage.removeItem("mx_fresh_login");
logger.log(`Restoring session for ${userId}`);
2020-10-07 12:32:30 +00:00
await doSetLoggedIn(
{
2018-04-27 16:49:53 +00:00
userId: userId,
deviceId: deviceId,
accessToken: decryptedAccessToken!,
refreshToken: decryptedRefreshToken,
2018-04-27 16:49:53 +00:00
homeserverUrl: hsUrl,
identityServerUrl: isUrl,
guest: isGuest,
pickleKey: pickleKey ?? undefined,
2020-09-30 04:52:47 +00:00
freshLogin: freshLogin,
2018-04-27 16:49:53 +00:00
},
false,
);
return true;
} else {
logger.log("No previous session found.");
2018-04-27 16:49:53 +00:00
return false;
}
}
async function handleLoadSessionFailure(e: unknown): Promise<boolean> {
logger.error("Unable to load session", e);
const modal = Modal.createDialog(SessionRestoreErrorDialog, {
error: e,
});
const [success] = await modal.finished;
if (success) {
// user clicked continue.
2020-10-07 12:32:30 +00:00
await clearStorage();
return false;
}
// try, try again
return loadSession();
}
/**
* Transitions to a logged-in state using the given credentials.
*
* Starts the matrix client and all other react-sdk services that
* listen for events while a session is logged in.
*
* Also stops the old MatrixClient and clears old credentials/etc out of
* storage before starting the new client.
*
* @param {IMatrixClientCreds} credentials The credentials to use
*
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
*/
2020-10-07 11:14:36 +00:00
export async function setLoggedIn(credentials: IMatrixClientCreds): Promise<MatrixClient> {
2020-09-30 04:52:47 +00:00
credentials.freshLogin = true;
stopMatrixClient();
const pickleKey =
credentials.userId && credentials.deviceId
? await PlatformPeg.get()?.createPickleKey(credentials.userId, credentials.deviceId)
2020-10-07 11:14:36 +00:00
: null;
if (pickleKey) {
logger.log("Created pickle key");
} else {
logger.log("Pickle key not created");
}
2021-06-29 12:11:58 +00:00
return doSetLoggedIn(Object.assign({}, credentials, { pickleKey }), true);
}
/**
* Hydrates an existing session by using the credentials provided. This will
* not clear any local storage, unlike setLoggedIn().
*
* Stops the existing Matrix client (without clearing its data) and starts a
* new one in its place. This additionally starts all other react-sdk services
* which use the new Matrix client.
*
* If the credentials belong to a different user from the session already stored,
* the old session will be cleared automatically.
*
* @param {IMatrixClientCreds} credentials The credentials to use
*
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
*/
2022-01-20 20:54:25 +00:00
export async function hydrateSession(credentials: IMatrixClientCreds): Promise<MatrixClient> {
const oldUserId = MatrixClientPeg.safeGet().getUserId();
const oldDeviceId = MatrixClientPeg.safeGet().getDeviceId();
stopMatrixClient(); // unsets MatrixClientPeg.get()
localStorage.removeItem("mx_soft_logout");
_isLoggingOut = false;
const overwrite = credentials.userId !== oldUserId || credentials.deviceId !== oldDeviceId;
if (overwrite) {
logger.warn("Clearing all data: Old session belongs to a different user/session");
}
if (!credentials.pickleKey && credentials.deviceId !== undefined) {
2022-01-20 20:54:25 +00:00
logger.info("Lifecycle#hydrateSession: Pickle key not provided - trying to get one");
credentials.pickleKey =
(await PlatformPeg.get()?.getPickleKey(credentials.userId, credentials.deviceId)) ?? undefined;
2022-01-20 20:54:25 +00:00
}
2020-10-07 12:32:30 +00:00
return doSetLoggedIn(credentials, overwrite);
}
/**
* When we have a authenticated via OIDC-native flow and have a refresh token
* try to create a token refresher.
* @param credentials from current session
* @returns Promise that resolves to a TokenRefresher, or undefined
*/
async function createOidcTokenRefresher(credentials: IMatrixClientCreds): Promise<OidcTokenRefresher | undefined> {
if (!credentials.refreshToken) {
return;
}
// stored token issuer indicates we authenticated via OIDC-native flow
const tokenIssuer = getStoredOidcTokenIssuer();
if (!tokenIssuer) {
return;
}
try {
const clientId = getStoredOidcClientId();
const idTokenClaims = getStoredOidcIdTokenClaims();
const redirectUri = PlatformPeg.get()!.getOidcCallbackUrl().href;
const deviceId = credentials.deviceId;
if (!deviceId) {
throw new Error("Expected deviceId in user credentials.");
}
const tokenRefresher = new TokenRefresher(
tokenIssuer,
clientId,
redirectUri,
deviceId,
idTokenClaims!,
credentials.userId,
);
// wait for the OIDC client to initialise
await tokenRefresher.oidcClientReady;
return tokenRefresher;
} catch (error) {
logger.error("Failed to initialise OIDC token refresher", error);
}
}
/**
* optionally clears localstorage, persists new credentials
* to localstorage, starts the new client.
*
* @param {IMatrixClientCreds} credentials
* @param {Boolean} clearStorageEnabled
*
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
*/
2020-10-07 11:14:36 +00:00
async function doSetLoggedIn(credentials: IMatrixClientCreds, clearStorageEnabled: boolean): Promise<MatrixClient> {
checkSessionLock();
credentials.guest = Boolean(credentials.guest);
const softLogout = isSoftLogout();
logger.log(
"setLoggedIn: mxid: " +
credentials.userId +
" deviceId: " +
credentials.deviceId +
" guest: " +
credentials.guest +
" hs: " +
credentials.homeserverUrl +
" softLogout: " +
softLogout,
" freshLogin: " + credentials.freshLogin,
);
2020-10-07 14:10:11 +00:00
if (clearStorageEnabled) {
2020-10-07 12:32:30 +00:00
await clearStorage();
}
const results = await StorageManager.checkConsistency();
// If there's an inconsistency between account data in local storage and the
// crypto store, we'll be generally confused when handling encrypted data.
// Show a modal recommending a full reset of storage.
if (results.dataInLocalStorage && results.cryptoInited && !results.dataInCryptoStore) {
await abortLogin();
}
const tokenRefresher = await createOidcTokenRefresher(credentials);
// check the session lock just before creating the new client
checkSessionLock();
MatrixClientPeg.replaceUsingCreds(credentials, tokenRefresher?.doRefreshAccessToken.bind(tokenRefresher));
const client = MatrixClientPeg.safeGet();
setSentryUser(credentials.userId);
if (PosthogAnalytics.instance.isEnabled()) {
PosthogAnalytics.instance.startListeningToSettingsChanges(client);
}
2020-09-30 04:52:47 +00:00
2020-10-02 21:43:49 +00:00
if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) {
2020-09-30 04:52:47 +00:00
// If we just logged in, try to rehydrate a device instead of using a
// new device. If it succeeds, we'll get a new device ID, so make sure
// we persist that ID to localStorage
const newDeviceId = await client.rehydrateDevice();
if (newDeviceId) {
credentials.deviceId = newDeviceId;
}
delete credentials.freshLogin;
}
if (localStorage) {
try {
await persistCredentials(credentials);
// make sure we don't think that it's a fresh login any more
2020-09-30 04:52:47 +00:00
sessionStorage.removeItem("mx_fresh_login");
} catch (e) {
logger.warn("Error using local storage: can't persist session!", e);
}
} else {
logger.warn("No local storage available: can't persist session!");
}
checkSessionLock();
dis.fire(Action.OnLoggedIn);
const clientPegOpts: MatrixClientPegAssignOpts = {};
if (credentials.pickleKey) {
// The pickleKey, if provided, is probably a base64-encoded 256-bit key, so can be used for the crypto store.
if (credentials.pickleKey.length === 43) {
clientPegOpts.rustCryptoStoreKey = decodeBase64(credentials.pickleKey);
} else {
// We have some legacy pickle key. Continue using it as a password.
clientPegOpts.rustCryptoStorePassword = credentials.pickleKey;
}
}
try {
await startMatrixClient(client, /*startSyncing=*/ !softLogout, clientPegOpts);
} finally {
clientPegOpts.rustCryptoStoreKey?.fill(0);
}
2020-09-30 04:52:47 +00:00
return client;
}
async function showStorageEvictedDialog(): Promise<boolean> {
const { finished } = Modal.createDialog(StorageEvictedDialog);
const [ok] = await finished;
return !!ok;
}
// Note: Babel 6 requires the `transform-builtin-extend` plugin for this to satisfy
// `instanceof`. Babel 7 supports this natively in their class handling.
class AbortLoginAndRebuildStorage extends Error {}
async function persistCredentials(credentials: IMatrixClientCreds): Promise<void> {
localStorage.setItem(HOMESERVER_URL_KEY, credentials.homeserverUrl);
if (credentials.identityServerUrl) {
localStorage.setItem(ID_SERVER_URL_KEY, credentials.identityServerUrl);
}
localStorage.setItem("mx_user_id", credentials.userId);
localStorage.setItem("mx_is_guest", JSON.stringify(credentials.guest));
await persistAccessTokenInStorage(credentials.accessToken, credentials.pickleKey);
await persistRefreshTokenInStorage(credentials.refreshToken, credentials.pickleKey);
if (credentials.pickleKey) {
localStorage.setItem("mx_has_pickle_key", String(true));
} else {
if (localStorage.getItem("mx_has_pickle_key") === "true") {
logger.error("Expected a pickle key, but none provided. Encryption may not work.");
}
}
// if we didn't get a deviceId from the login, leave mx_device_id unset,
// rather than setting it to "undefined".
//
// (in this case MatrixClient doesn't bother with the crypto stuff
// - that's fine for us).
if (credentials.deviceId) {
localStorage.setItem("mx_device_id", credentials.deviceId);
}
Replace `SecurityCustomisations` with `CryptoSetupExtension` (#12342) * Changed call sites from customisations/security to ModuleRunner.extensions * Updated depenndecy and added tests * Fixed style and formatting with prettier * Fix according to Element PR comments * Fixing issues raised in PR review * Removed commented code. Improved encapsulation. Removed noisy logging * Improved language of comment about calling the factory * Refactor to get better encapsulation * Find a better name. Provide explicit reset function. Provide more TSDoc * Simplify mock for cryptoSetup, and add assertion for exception message. * Remove unused className property. Adjust TSDoc comments * Fix linting and code style issues * Added test to ensure we canregister anduse experimental extensions * Fix linting and code-style issues * Added test to ensure only on registration of experimental extensions * Added test toensure call to getDehydratedDeviceCallback() * Test what happens when there is no implementation * Iterating cryptoSetup tests * Lint/prettier fix * Assert both branches when checking for dehydrationkey callback * Update src/modules/ModuleRunner.ts Language and formatting Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Reset by setting a fresh ExtensionsManager Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Use regular comment instead of TSDoc style comment Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update test/MatrixClientPeg-test.ts No need to extend the base class Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix spelling Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix spelling Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update src/modules/ModuleRunner.ts Fix TSDoc formatting Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Simplify mock setup * Simplified mock and cleaned up a bit * Keeping track of extensions is an implementation detail internal to ExtensionsManager. Language and punctuation * Addressed issues and comments from PR review * Update src/modules/ModuleRunner.ts Keep the flags to track implementations as direct properties Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Fix flattening of implementation map * Update src/modules/ModuleRunner.ts Fix whitespace Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2024-04-12 15:15:17 +00:00
ModuleRunner.instance.extensions.cryptoSetup?.persistCredentials(credentials);
logger.log(`Session persisted for ${credentials.userId}`);
}
let _isLoggingOut = false;
OIDC: revoke tokens on logout (#11718) * test persistCredentials without a pickle key * test setLoggedIn with pickle key * lint * type error * extract token persisting code into function, persist refresh token * store has_refresh_token too * pass refreshToken from oidcAuthGrant into credentials * rest restore session with pickle key * retreive stored refresh token and add to credentials * extract token decryption into function * remove TODO * very messy poc * utils to persist clientId and issuer after oidc authentication * add dep oidc-client-ts * persist issuer and clientId after successful oidc auth * add OidcClientStore * comments and tidy * expose getters for stored refresh and access tokens in Lifecycle * revoke tokens with oidc provider * test logout action in MatrixChat * comments * prettier * test OidcClientStore.revokeTokens * put pickle key destruction back * comment pedantry * working refresh without persistence * extract token persistence functions to utils * add sugar * implement TokenRefresher class with persistence * tidying * persist idTokenClaims * persist idTokenClaims * tests * remove unused cde * create token refresher during doSetLoggedIn * tidying * also tidying * OidcClientStore.initClient use stored issuer when client well known unavailable * test Lifecycle.logout * update Lifecycle test replaceUsingCreds calls * fix test * tidy * test tokenrefresher creation in login flow * test token refresher * Update src/utils/oidc/TokenRefresher.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * use literal value for m.authentication Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * improve comments * fix test mock, comment * typo * add sdkContext to SoftLogout, pass oidcClientStore to logout * fullstops * comments * fussy comment formatting --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-15 21:35:25 +00:00
/**
* Logs out the current session.
* When user has authenticated using OIDC native flow revoke tokens with OIDC provider.
* Otherwise, call /logout on the homeserver.
* @param client
* @param oidcClientStore
*/
async function doLogout(client: MatrixClient, oidcClientStore?: OidcClientStore): Promise<void> {
if (oidcClientStore?.isUserAuthenticatedWithOidc) {
const accessToken = client.getAccessToken() ?? undefined;
const refreshToken = client.getRefreshToken() ?? undefined;
await oidcClientStore.revokeTokens(accessToken, refreshToken);
} else {
await client.logout(true);
}
}
2016-08-02 17:55:13 +00:00
/**
* Logs the current session out and transitions to the logged-out state
OIDC: revoke tokens on logout (#11718) * test persistCredentials without a pickle key * test setLoggedIn with pickle key * lint * type error * extract token persisting code into function, persist refresh token * store has_refresh_token too * pass refreshToken from oidcAuthGrant into credentials * rest restore session with pickle key * retreive stored refresh token and add to credentials * extract token decryption into function * remove TODO * very messy poc * utils to persist clientId and issuer after oidc authentication * add dep oidc-client-ts * persist issuer and clientId after successful oidc auth * add OidcClientStore * comments and tidy * expose getters for stored refresh and access tokens in Lifecycle * revoke tokens with oidc provider * test logout action in MatrixChat * comments * prettier * test OidcClientStore.revokeTokens * put pickle key destruction back * comment pedantry * working refresh without persistence * extract token persistence functions to utils * add sugar * implement TokenRefresher class with persistence * tidying * persist idTokenClaims * persist idTokenClaims * tests * remove unused cde * create token refresher during doSetLoggedIn * tidying * also tidying * OidcClientStore.initClient use stored issuer when client well known unavailable * test Lifecycle.logout * update Lifecycle test replaceUsingCreds calls * fix test * tidy * test tokenrefresher creation in login flow * test token refresher * Update src/utils/oidc/TokenRefresher.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * use literal value for m.authentication Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * improve comments * fix test mock, comment * typo * add sdkContext to SoftLogout, pass oidcClientStore to logout * fullstops * comments * fussy comment formatting --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-15 21:35:25 +00:00
* @param oidcClientStore store instance from SDKContext
2016-08-02 17:55:13 +00:00
*/
OIDC: revoke tokens on logout (#11718) * test persistCredentials without a pickle key * test setLoggedIn with pickle key * lint * type error * extract token persisting code into function, persist refresh token * store has_refresh_token too * pass refreshToken from oidcAuthGrant into credentials * rest restore session with pickle key * retreive stored refresh token and add to credentials * extract token decryption into function * remove TODO * very messy poc * utils to persist clientId and issuer after oidc authentication * add dep oidc-client-ts * persist issuer and clientId after successful oidc auth * add OidcClientStore * comments and tidy * expose getters for stored refresh and access tokens in Lifecycle * revoke tokens with oidc provider * test logout action in MatrixChat * comments * prettier * test OidcClientStore.revokeTokens * put pickle key destruction back * comment pedantry * working refresh without persistence * extract token persistence functions to utils * add sugar * implement TokenRefresher class with persistence * tidying * persist idTokenClaims * persist idTokenClaims * tests * remove unused cde * create token refresher during doSetLoggedIn * tidying * also tidying * OidcClientStore.initClient use stored issuer when client well known unavailable * test Lifecycle.logout * update Lifecycle test replaceUsingCreds calls * fix test * tidy * test tokenrefresher creation in login flow * test token refresher * Update src/utils/oidc/TokenRefresher.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * use literal value for m.authentication Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * improve comments * fix test mock, comment * typo * add sdkContext to SoftLogout, pass oidcClientStore to logout * fullstops * comments * fussy comment formatting --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-15 21:35:25 +00:00
export function logout(oidcClientStore?: OidcClientStore): void {
const client = MatrixClientPeg.get();
if (!client) return;
2017-12-05 11:38:25 +00:00
PosthogAnalytics.instance.logout();
if (client.isGuest()) {
// logout doesn't work for guest sessions
// Also we sometimes want to re-log in a guest session if we abort the login.
// defer until next tick because it calls a synchronous dispatch, and we are likely here from a dispatch.
setImmediate(() => onLoggedOut());
return;
}
_isLoggingOut = true;
PlatformPeg.get()?.destroyPickleKey(client.getSafeUserId(), client.getDeviceId() ?? "");
OIDC: revoke tokens on logout (#11718) * test persistCredentials without a pickle key * test setLoggedIn with pickle key * lint * type error * extract token persisting code into function, persist refresh token * store has_refresh_token too * pass refreshToken from oidcAuthGrant into credentials * rest restore session with pickle key * retreive stored refresh token and add to credentials * extract token decryption into function * remove TODO * very messy poc * utils to persist clientId and issuer after oidc authentication * add dep oidc-client-ts * persist issuer and clientId after successful oidc auth * add OidcClientStore * comments and tidy * expose getters for stored refresh and access tokens in Lifecycle * revoke tokens with oidc provider * test logout action in MatrixChat * comments * prettier * test OidcClientStore.revokeTokens * put pickle key destruction back * comment pedantry * working refresh without persistence * extract token persistence functions to utils * add sugar * implement TokenRefresher class with persistence * tidying * persist idTokenClaims * persist idTokenClaims * tests * remove unused cde * create token refresher during doSetLoggedIn * tidying * also tidying * OidcClientStore.initClient use stored issuer when client well known unavailable * test Lifecycle.logout * update Lifecycle test replaceUsingCreds calls * fix test * tidy * test tokenrefresher creation in login flow * test token refresher * Update src/utils/oidc/TokenRefresher.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * use literal value for m.authentication Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * improve comments * fix test mock, comment * typo * add sdkContext to SoftLogout, pass oidcClientStore to logout * fullstops * comments * fussy comment formatting --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-10-15 21:35:25 +00:00
doLogout(client, oidcClientStore).then(onLoggedOut, (err) => {
// Just throwing an error here is going to be very unhelpful
// if you're trying to log out because your server's down and
// you want to log into a different server, so just forget the
// access token. It's annoying that this will leave the access
// token still valid, but we should fix this by having access
// tokens expire (and if you really think you've been compromised,
// change your password).
logger.warn("Failed to call logout API: token will not be invalidated", err);
onLoggedOut();
});
}
2020-10-07 11:14:36 +00:00
export function softLogout(): void {
if (!MatrixClientPeg.get()) return;
// Track that we've detected and trapped a soft logout. This helps prevent other
// parts of the app from starting if there's no point (ie: don't sync if we've
// been soft logged out, despite having credentials and data for a MatrixClient).
localStorage.setItem("mx_soft_logout", "true");
2019-10-02 15:45:53 +00:00
// Dev note: please keep this log line around. It can be useful for track down
// random clients stopping in the middle of the logs.
logger.log("Soft logout initiated");
_isLoggingOut = true; // to avoid repeated flags
// Ensure that we dispatch a view change **before** stopping the client so
// so that React components unmount first. This avoids React soft crashes
// that can occur when components try to use a null client.
2021-06-29 12:11:58 +00:00
dis.dispatch({ action: "on_client_not_viable" }); // generic version of on_logged_out
stopMatrixClient(/*unsetClient=*/ false);
// DO NOT CALL LOGOUT. A soft logout preserves data, logout does not.
}
2020-10-07 11:14:36 +00:00
export function isSoftLogout(): boolean {
return localStorage.getItem("mx_soft_logout") === "true";
}
2020-10-07 11:14:36 +00:00
export function isLoggingOut(): boolean {
return _isLoggingOut;
}
2016-08-02 17:56:12 +00:00
/**
* Starts the matrix client and all other react-sdk services that
* listen for events while a session is logged in.
*
* @param client the matrix client to start
* @param startSyncing - `true` to actually start syncing the client.
* @param clientPegOpts - Options to pass through to {@link MatrixClientPeg.start}.
2016-08-02 17:56:12 +00:00
*/
async function startMatrixClient(
client: MatrixClient,
startSyncing: boolean,
clientPegOpts: MatrixClientPegAssignOpts,
): Promise<void> {
logger.log(`Lifecycle: Starting MatrixClient`);
// dispatch this before starting the matrix client: it's used
// to add listeners for the 'sync' event so otherwise we'd have
// a race condition (and we need to dispatch synchronously for this
// to work).
2021-06-29 12:11:58 +00:00
dis.dispatch({ action: "will_start_client" }, true);
// reset things first just in case
SdkContextClass.instance.typingStore.reset();
ToastStore.sharedInstance().reset();
DialogOpener.instance.prepare(client);
Notifier.start();
UserActivity.sharedInstance().start();
DMRoomMap.makeShared(client).start();
IntegrationManagers.sharedInstance().startWatching();
ActiveWidgetStore.instance.start();
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
LegacyCallHandler.instance.start();
// Start Mjolnir even though we haven't checked the feature flag yet. Starting
// the thing just wastes CPU cycles, but should result in no actual functionality
// being exposed to the user.
Mjolnir.sharedInstance().start();
if (startSyncing) {
// The client might want to populate some views with events from the
// index (e.g. the FilePanel), therefore initialize the event index
// before the client.
await EventIndexPeg.init();
await MatrixClientPeg.start(clientPegOpts);
} else {
logger.warn("Caller requested only auxiliary services be started");
await MatrixClientPeg.assign(clientPegOpts);
}
checkSessionLock();
// Run the migrations after the MatrixClientPeg has been assigned
SettingsStore.runMigrations();
// This needs to be started after crypto is set up
DeviceListener.sharedInstance().start(client);
// Similarly, don't start sending presence updates until we've started
// the client
if (!SettingsStore.getValue("lowBandwidth")) {
Presence.start();
}
// Now that we have a MatrixClientPeg, update the Jitsi info
Jitsi.getInstance().start();
// dispatch that we finished starting up to wire up any other bits
// of the matrix client that cannot be set prior to starting up.
2021-06-29 12:11:58 +00:00
dis.dispatch({ action: "client_started" });
if (isSoftLogout()) {
softLogout();
}
}
/*
* Stops a running client and all related services, and clears persistent
* storage. Used after a session has been logged out.
*/
2020-10-07 11:14:36 +00:00
export async function onLoggedOut(): Promise<void> {
// Ensure that we dispatch a view change **before** stopping the client,
// that React components unmount first. This avoids React soft crashes
// that can occur when components try to use a null client.
dis.fire(Action.OnLoggedOut, true);
stopMatrixClient();
2021-06-29 12:11:58 +00:00
await clearStorage({ deleteEverything: true });
LifecycleCustomisations.onLoggedOutAndStorageCleared?.();
await PlatformPeg.get()?.clearStorage();
// Do this last, so we can make sure all storage has been cleared and all
// customisations got the memo.
if (SdkConfig.get().logout_redirect_url) {
logger.log("Redirecting to external provider to finish logout");
// XXX: Defer this so that it doesn't race with MatrixChat unmounting the world by going to /#/login
window.setTimeout(() => {
window.location.href = SdkConfig.get().logout_redirect_url!;
}, 100);
}
// Do this last to prevent racing `stopMatrixClient` and `on_logged_out` with MatrixChat handling Session.logged_out
_isLoggingOut = false;
}
/**
2020-09-12 03:09:42 +00:00
* @param {object} opts Options for how to clear storage.
* @returns {Promise} promise which resolves once the stores have been cleared
*/
2020-10-07 12:32:30 +00:00
async function clearStorage(opts?: { deleteEverything?: boolean }): Promise<void> {
if (window.localStorage) {
// get the currently defined device language, if set, so we can restore it later
const language = SettingsStore.getValueAt(SettingLevel.DEVICE, "language", null, true, true);
// try to save any 3pid invites from being obliterated and registration time
const pendingInvites = ThreepidInviteStore.instance.getWireInvites();
const registrationTime = window.localStorage.getItem("mx_registration_time");
window.localStorage.clear();
AbstractLocalStorageSettingsHandler.clear();
try {
await StorageAccess.idbDelete("account", ACCESS_TOKEN_STORAGE_KEY);
} catch (e) {
logger.error("idbDelete failed for account:mx_access_token", e);
}
// now restore those invites, registration time and previously set device language
if (!opts?.deleteEverything) {
if (language) {
await SettingsStore.setValue("language", null, SettingLevel.DEVICE, language);
}
pendingInvites.forEach(({ roomId, ...invite }) => {
ThreepidInviteStore.instance.storeInvite(roomId, invite);
});
if (registrationTime) {
window.localStorage.setItem("mx_registration_time", registrationTime);
}
}
}
window.sessionStorage?.clear();
// create a temporary client to clear out the persistent stores.
const cli = createMatrixClient({
// we'll never make any requests, so can pass a bogus HS URL
baseUrl: "",
});
await EventIndexPeg.deleteEventIndex();
await cli.clearStores();
}
/**
* Stop all the background processes related to the current client.
* @param {boolean} unsetClient True (default) to abandon the client
* on MatrixClientPeg after stopping.
*/
2020-10-07 11:14:36 +00:00
export function stopMatrixClient(unsetClient = true): void {
Notifier.stop();
Prepare for Element Call integration (#9224) * Improve accessibility and testability of Tooltip Adding a role to Tooltip was motivated by React Testing Library's reliance on accessibility-related attributes to locate elements. * Make the ReadyWatchingStore constructor safer The ReadyWatchingStore constructor previously had a chance to immediately call onReady, which was dangerous because it was potentially calling the derived class's onReady at a point when the derived class hadn't even finished construction yet. In normal usage, I guess this never was a problem, but it was causing some of the tests I was writing to crash. This is solved by separating out the onReady call into a start method. * Rename 1:1 call components to 'LegacyCall' to reflect the fact that they're slated for removal, and to not clash with the new Call code. * Refactor VideoChannelStore into Call and CallStore Call is an abstract class that currently only has a Jitsi implementation, but this will make it easy to later add an Element Call implementation. * Remove WidgetReady, ClientReady, and ForceHangupCall hacks These are no longer used by the new Jitsi call implementation, and can be removed. * yarn i18n * Delete call map entries instead of inserting nulls * Allow multiple active calls and consolidate call listeners * Fix a race condition when creating a video room * Un-hardcode the media device fallback labels * Apply misc code review fixes * yarn i18n * Disconnect from calls more politely on logout * Fix some strict mode errors * Fix another updateRoom race condition
2022-08-30 19:13:39 +00:00
LegacyCallHandler.instance.stop();
2019-03-08 15:09:44 +00:00
UserActivity.sharedInstance().stop();
SdkContextClass.instance.typingStore.reset();
Presence.stop();
ActiveWidgetStore.instance.stop();
IntegrationManagers.sharedInstance().stopWatching();
Mjolnir.sharedInstance().stop();
DeviceListener.sharedInstance().stop();
DMRoomMap.shared()?.stop();
EventIndexPeg.stop();
2017-05-04 17:03:35 +00:00
const cli = MatrixClientPeg.get();
if (cli) {
cli.stopClient();
cli.removeAllListeners();
if (unsetClient) {
MatrixClientPeg.unset();
EventIndexPeg.unset();
cli.store.destroy();
}
}
}
// Utility method to perform a login with an existing access_token
window.mxLoginWithAccessToken = async (hsUrl: string, accessToken: string): Promise<void> => {
const tempClient = createClient({
baseUrl: hsUrl,
accessToken,
});
const { user_id: userId } = await tempClient.whoami();
await doSetLoggedIn(
{
homeserverUrl: hsUrl,
accessToken,
userId,
},
true,
);
};