Files
ThreadNet-Web/src/CreateCrossSigning.ts
T
19ef3267c0 Refactor CreateCrossSigningDialog (#28218)
* Refactor CreateCrossSigningDialog

 * Converts CreateCrossSigningDialog to a functional component
 * Pulls logic out to its own class
 * Updates usage of deprecated cross signing bootstrap method on client to be on the crypto object and updates test to match

Moved from https://github.com/element-hq/matrix-react-sdk/pull/131

* Add mock here too

* Use the right mock

* Remove duplicate mock

* Stray jest mock line

* Un-move mocks

* tsdoc

* Typo

Co-authored-by: Andy Balaam <andy.balaam@matrix.org>

---------

Co-authored-by: Andy Balaam <andy.balaam@matrix.org>
2024-10-22 11:42:07 +00:00

119 lines
4.7 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2018, 2019 New Vector Ltd
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { logger } from "matrix-js-sdk/src/logger";
import { AuthDict, CrossSigningKeys, MatrixClient, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix";
import { SSOAuthEntry } from "./components/views/auth/InteractiveAuthEntryComponents";
import Modal from "./Modal";
import { _t } from "./languageHandler";
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
/**
* Determine if the homeserver allows uploading device keys with only password auth.
* @param cli The Matrix Client to use
* @returns True if the homeserver allows uploading device keys with only password auth, otherwise false
*/
async function canUploadKeysWithPasswordOnly(cli: MatrixClient): Promise<boolean> {
try {
await cli.uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys);
// We should never get here: the server should always require
// UI auth to upload device signing keys. If we do, we upload
// no keys which would be a no-op.
logger.log("uploadDeviceSigningKeys unexpectedly succeeded without UI auth!");
return false;
} catch (error) {
if (!(error instanceof MatrixError) || !error.data || !error.data.flows) {
logger.log("uploadDeviceSigningKeys advertised no flows!");
return false;
}
const canUploadKeysWithPasswordOnly = error.data.flows.some((f: UIAFlow) => {
return f.stages.length === 1 && f.stages[0] === "m.login.password";
});
return canUploadKeysWithPasswordOnly;
}
}
/**
* Ensures that cross signing keys are created and uploaded for the user.
* The homeserver may require user-interactive auth to upload the keys, in
* which case the user will be prompted to authenticate. If the homeserver
* allows uploading keys with just an account password and one is provided,
* the keys will be uploaded without user interaction.
*
* This function does not set up backups of the created cross-signing keys
* (or message keys): the cross-signing keys are stored locally and will be
* lost requiring a crypto reset, if the user logs out or loses their session.
*
* @param cli The Matrix Client to use
* @param isTokenLogin True if the user logged in via a token login, otherwise false
* @param accountPassword The password that the user logged in with
*/
export async function createCrossSigning(
cli: MatrixClient,
isTokenLogin: boolean,
accountPassword?: string,
): Promise<void> {
const cryptoApi = cli.getCrypto();
if (!cryptoApi) {
throw new Error("No crypto API found!");
}
const doBootstrapUIAuth = async (
makeRequest: (authData: AuthDict) => Promise<UIAResponse<void>>,
): Promise<void> => {
if (accountPassword && (await canUploadKeysWithPasswordOnly(cli))) {
await makeRequest({
type: "m.login.password",
identifier: {
type: "m.id.user",
user: cli.getUserId(),
},
password: accountPassword,
});
} else if (isTokenLogin) {
// We are hoping the grace period is active
await makeRequest({});
} else {
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("auth|uia|sso_title"),
body: _t("auth|uia|sso_preauth_body"),
continueText: _t("auth|sso"),
continueKind: "primary",
},
[SSOAuthEntry.PHASE_POSTAUTH]: {
title: _t("encryption|confirm_encryption_setup_title"),
body: _t("encryption|confirm_encryption_setup_body"),
continueText: _t("action|confirm"),
continueKind: "primary",
},
};
const { finished } = Modal.createDialog(InteractiveAuthDialog, {
title: _t("encryption|bootstrap_title"),
matrixClient: cli,
makeRequest,
aestheticsForStagePhases: {
[SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
},
});
const [confirmed] = await finished;
if (!confirmed) {
throw new Error("Cross-signing key upload auth canceled");
}
}
};
await cryptoApi.bootstrapCrossSigning({
authUploadDeviceSigningKeys: doBootstrapUIAuth,
});
}