element-web/src/components/views/settings/encryption/ResetIdentityPanel.tsx

145 lines
5.4 KiB
TypeScript

/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import { Breadcrumb, Button, VisualList, VisualListItem } from "@vector-im/compound-web";
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error";
import React, { MouseEventHandler } from "react";
import { AuthDict, MatrixClient, UIAResponse } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import { EncryptionCard } from "./EncryptionCard";
import { withSecretStorageKeyCache } from "../../../../SecurityManager";
import Modal from "../../../../Modal";
import InteractiveAuthDialog from "../../dialogs/InteractiveAuthDialog";
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
import { SSOAuthEntry } from "../../auth/InteractiveAuthEntryComponents";
interface ResetIdentityPanelProps {
/**
* Called when the identity is reset.
*/
onFinish: MouseEventHandler<HTMLButtonElement>;
/**
* Called when the cancel button is clicked or when we go back in the breadcrumbs.
*/
onCancelClick: () => void;
}
/**
* The panel for resetting the identity of the current user.
*/
export function ResetIdentityPanel({ onCancelClick, onFinish }: ResetIdentityPanelProps): JSX.Element {
const matrixClient = useMatrixClientContext();
return (
<>
<Breadcrumb
backLabel={_t("action|back")}
onBackClick={onCancelClick}
pages={[_t("settings|encryption|title"), _t("settings|encryption|advanced|breadcrumb_page")]}
onPageClick={onCancelClick}
/>
<EncryptionCard
Icon={ErrorIcon}
destructive={true}
title={_t("settings|encryption|advanced|breadcrumb_title")}
className="mx_ResetIdentityPanel"
>
<div className="mx_ResetIdentityPanel_content">
<VisualList>
<VisualListItem Icon={CheckIcon} success={true}>
{_t("settings|encryption|advanced|breadcrumb_first_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_second_description")}
</VisualListItem>
<VisualListItem Icon={InfoIcon}>
{_t("settings|encryption|advanced|breadcrumb_third_description")}
</VisualListItem>
</VisualList>
<span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>
</div>
<div className="mx_ResetIdentityPanel_footer">
<Button
destructive={true}
onClick={async (evt) => {
await resetIdentity(matrixClient);
onFinish(evt);
}}
>
{_t("action|continue")}
</Button>
<Button kind="tertiary" onClick={onCancelClick}>
{_t("action|cancel")}
</Button>
</div>
</EncryptionCard>
</>
);
}
/**
* Resets the identity of the current user.
*/
async function resetIdentity(matrixClient: MatrixClient): Promise<void> {
const crypto = matrixClient.getCrypto();
if (!crypto) return;
await withSecretStorageKeyCache(async () => {
await crypto.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (makeRequest): Promise<void> =>
uiAuthCallback(matrixClient, makeRequest),
setupNewCrossSigning: true,
});
await crypto.bootstrapSecretStorage({
setupNewKeyBackup: true,
});
});
}
/**
* Handles the UIA flow for resetting the identity.
* @param matrixClient
* @param makeRequest
*/
async function uiAuthCallback(
matrixClient: MatrixClient,
makeRequest: (authData: AuthDict) => Promise<UIAResponse<void>>,
): Promise<void> {
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,
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");
}
}