Add device verification

florianduros/encryption-tab
Florian Duros 2024-12-12 17:16:49 +01:00
parent f9e48b4e3b
commit 0057f57db9
No known key found for this signature in database
GPG Key ID: A5BBB4041B493F15
2 changed files with 87 additions and 3 deletions

View File

@ -5,19 +5,42 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
import React, { JSX, useState } from "react"; import React, { JSX, useCallback, useEffect, useState } from "react";
import { Button, InlineSpinner } from "@vector-im/compound-web";
import ComputerIcon from "@vector-im/compound-design-tokens/assets/web/icons/computer";
import SettingsTab from "../SettingsTab"; import SettingsTab from "../SettingsTab";
import { RecoveryPanel } from "../../encryption/RecoveryPanel"; import { RecoveryPanel } from "../../encryption/RecoveryPanel";
import { ChangeRecoveryKey } from "../../encryption/ChangeRecoveryKey"; import { ChangeRecoveryKey } from "../../encryption/ChangeRecoveryKey";
import { useMatrixClientContext } from "../../../../../contexts/MatrixClientContext";
import { _t } from "../../../../../languageHandler";
import Modal from "../../../../../Modal";
import SetupEncryptionDialog from "../../../dialogs/security/SetupEncryptionDialog";
import { SettingsSection } from "../../shared/SettingsSection";
import { SettingsSubheader } from "../../SettingsSubheader";
type Panel = "main" | "change_recovery_key" | "set_recovery_key"; /**
* The panel to show in the encryption settings tab.
* - "loading": We are checking if the device is verified.
* - "main": The main panel with all the sections (Key storage, recovery, advanced).
* - "verification_required": The panel to show when the user needs to verify their session.
* - "change_recovery_key": The panel to show when the user is changing their recovery key.
* - "set_recovery_key": The panel to show when the user is setting up their recovery key.
*/
type Panel = "loading" | "main" | "verification_required" | "change_recovery_key" | "set_recovery_key";
export function EncryptionUserSettingsTab(): JSX.Element { export function EncryptionUserSettingsTab(): JSX.Element {
const [panel, setPanel] = useState<Panel>("main"); const [panel, setPanel] = useState<Panel>("loading");
const checkVerificationRequired = useVerificationRequired(setPanel);
let content: JSX.Element; let content: JSX.Element;
switch (panel) { switch (panel) {
case "loading":
content = <InlineSpinner />;
break;
case "verification_required":
content = <VerifySessionPanel onFinish={checkVerificationRequired} />;
break;
case "main": case "main":
content = ( content = (
<RecoveryPanel <RecoveryPanel
@ -48,3 +71,61 @@ export function EncryptionUserSettingsTab(): JSX.Element {
return <SettingsTab className="mx_EncryptionUserSettingsTab">{content}</SettingsTab>; return <SettingsTab className="mx_EncryptionUserSettingsTab">{content}</SettingsTab>;
} }
/**
* Hook to check if the user needs to verify their session.
* If the user needs to verify their session, the panel will be set to "verification_required".
* If the user doesn't need to verify their session, the panel will be set to "main".
* @param setPanel
*/
function useVerificationRequired(setPanel: (panel: Panel) => void): () => Promise<void> {
const matrixClient = useMatrixClientContext();
const checkVerificationRequired = useCallback(async () => {
const crypto = matrixClient.getCrypto();
if (!crypto) return;
const isCrossSigningReady = await crypto.isCrossSigningReady();
if (isCrossSigningReady) setPanel("main");
else setPanel("verification_required");
}, [matrixClient, setPanel]);
useEffect(() => {
checkVerificationRequired();
}, [checkVerificationRequired]);
return checkVerificationRequired;
}
interface VerifySessionPanelProps {
/**
* Callback to call when the user has finished verifying their session.
*/
onFinish: () => void;
}
/**
* Panel to show when the user needs to verify their session.
*/
function VerifySessionPanel({ onFinish }: VerifySessionPanelProps): JSX.Element {
return (
<SettingsSection
legacy={false}
heading={_t("settings|encryption|device_not_verified_title")}
subHeading={
<SettingsSubheader
stateMessage={_t("settings|encryption|device_not_verified_description")}
state="error"
/>
}
>
<Button
size="sm"
Icon={ComputerIcon}
onClick={() => Modal.createDialog(SetupEncryptionDialog, { onFinished: onFinish })}
>
{_t("settings|encryption|device_not_verified_button")}
</Button>
</SettingsSection>
);
}

View File

@ -2463,6 +2463,9 @@
"enable_markdown": "Enable Markdown", "enable_markdown": "Enable Markdown",
"enable_markdown_description": "Start messages with <code>/plain</code> to send without markdown.", "enable_markdown_description": "Start messages with <code>/plain</code> to send without markdown.",
"encryption": { "encryption": {
"device_not_verified_button": "Verify this device",
"device_not_verified_description": "You need to verify this device in order to view your encryption settings.",
"device_not_verified_title": "Device not verified",
"dialog_title": "<strong>Settings:</strong> Encryption", "dialog_title": "<strong>Settings:</strong> Encryption",
"recovery": { "recovery": {
"change_recovery_key": "Change recovery key", "change_recovery_key": "Change recovery key",