Merge pull request #3962 from matrix-org/dbkr/better_flow_for_upgrade_on_login

Improve encryption upgrade on login flow
This commit is contained in:
David Baker 2020-01-28 18:05:09 +00:00 committed by GitHub
commit d1cbec98b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 62 deletions

View file

@ -25,15 +25,14 @@ import { _t } from '../../../../languageHandler';
import Modal from '../../../../Modal';
const PHASE_LOADING = 0;
const PHASE_RESTORE_KEY_BACKUP = 1;
const PHASE_MIGRATE = 2;
const PHASE_PASSPHRASE = 3;
const PHASE_PASSPHRASE_CONFIRM = 4;
const PHASE_SHOWKEY = 5;
const PHASE_KEEPITSAFE = 6;
const PHASE_STORING = 7;
const PHASE_DONE = 8;
const PHASE_OPTOUT_CONFIRM = 9;
const PHASE_MIGRATE = 1;
const PHASE_PASSPHRASE = 2;
const PHASE_PASSPHRASE_CONFIRM = 3;
const PHASE_SHOWKEY = 4;
const PHASE_KEEPITSAFE = 5;
const PHASE_STORING = 6;
const PHASE_DONE = 7;
const PHASE_CONFIRM_SKIP = 8;
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms.
@ -58,7 +57,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
accountPassword: PropTypes.string,
};
defaultProps = {
static defaultProps = {
hasCancel: true,
};
@ -110,9 +109,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
MatrixClientPeg.get().isCryptoEnabled() && await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo)
);
const phase = backupInfo ?
(backupSigStatus.usable ? PHASE_MIGRATE : PHASE_RESTORE_KEY_BACKUP) :
PHASE_PASSPHRASE;
const phase = backupInfo ? PHASE_MIGRATE : PHASE_PASSPHRASE;
this.setState({
phase,
@ -153,7 +150,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
_onMigrateFormSubmit = (e) => {
e.preventDefault();
this._bootstrapSecretStorage();
if (this.state.backupSigStatus.usable) {
this._bootstrapSecretStorage();
} else {
this._restoreBackup();
}
}
_onCopyClick = () => {
@ -228,6 +229,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
} catch (e) {
if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) {
this.setState({
accountPassword: '',
accountPasswordCorrect: false,
phase: PHASE_MIGRATE,
});
@ -246,16 +248,26 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
this.props.onFinished(true);
}
_onRestoreKeyBackupClick = () => {
_restoreBackup = async () => {
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
Modal.createTrackedDialog(
const { finished } = Modal.createTrackedDialog(
'Restore Backup', '', RestoreKeyBackupDialog, {showSummary: false}, null,
/* priority = */ false, /* static = */ true,
);
await finished;
await this._fetchBackupInfo();
if (
this.state.backupSigStatus.usable &&
this.state.canUploadKeysWithPasswordOnly &&
this.state.accountPassword
) {
this._bootstrapSecretStorage();
}
}
_onOptOutClick = () => {
this.setState({phase: PHASE_OPTOUT_CONFIRM});
_onSkipSetupClick = () => {
this.setState({phase: PHASE_CONFIRM_SKIP});
}
_onSetUpClick = () => {
@ -367,23 +379,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
});
}
_renderPhaseRestoreKeyBackup() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t(
"Key Backup is enabled on your account but has not been set " +
"up from this session. To set up secret storage, " +
"restore your key backup.",
)}</p>
<DialogButtons primaryButton={_t('Restore')}
onPrimaryButtonClick={this._onRestoreKeyBackupClick}
onCancel={this._onCancel}
hasCancel={true}
>
</DialogButtons>
</div>;
}
_renderPhaseMigrate() {
// TODO: This is a temporary screen so people who have the labs flag turned on and
// click the button are aware they're making a change to their account.
@ -394,7 +389,13 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
const Field = sdk.getComponent('views.elements.Field');
let authPrompt;
if (this.state.canUploadKeysWithPasswordOnly) {
let nextCaption = _t("Next");
if (!this.state.backupSigStatus.usable) {
authPrompt = <div>
<div>{_t("Restore your key backup to upgrade your encryption")}</div>
</div>;
nextCaption = _t("Restore");
} else if (this.state.canUploadKeysWithPasswordOnly) {
authPrompt = <div>
<div>{_t("Enter your account password to confirm the upgrade:")}</div>
<div><Field type="password"
@ -419,12 +420,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
"as trusted for other users.",
)}</p>
<div>{authPrompt}</div>
<DialogButtons primaryButton={_t('Next')}
<DialogButtons primaryButton={nextCaption}
primaryIsSubmit={true}
hasCancel={true}
onCancel={this._onCancel}
hasCancel={false}
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
/>
>
<button type="button" className="danger" onClick={this._onSkipClick}>
{_t('Skip')}
</button>
</DialogButtons>
</form>;
}
@ -486,7 +490,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
disabled={!this._passPhraseIsValid()}
>
<button type="button"
onClick={this._onCancel}
onClick={this._onSkipSetupClick}
className="danger"
>{_t("Skip")}</button>
</DialogButtons>
@ -554,7 +558,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
>
<button type="button"
onClick={this._onCancel}
onClick={this._onSkipSetupClick}
className="danger"
>{_t("Skip")}</button>
</DialogButtons>
@ -659,35 +663,32 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
</div>;
}
_renderPhaseOptOutConfirm() {
_renderPhaseSkipConfirm() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
{_t(
"Without setting up secret storage, you won't be able to restore your " +
"access to encrypted messages or your cross-signing identity for " +
"verifying other devices if you log out or use another device.",
"Without completing security on this device, it wont have " +
"access to encrypted messages.",
)}
<DialogButtons primaryButton={_t('Set up secret storage')}
<DialogButtons primaryButton={_t('Go back')}
onPrimaryButtonClick={this._onSetUpClick}
hasCancel={false}
>
<button onClick={this._onCancel}>I understand, continue without</button>
<button type="button" className="danger" onClick={this._onCancel}>{_t('Skip')}</button>
</DialogButtons>
</div>;
}
_titleForPhase(phase) {
switch (phase) {
case PHASE_RESTORE_KEY_BACKUP:
return _t('Restore your Key Backup');
case PHASE_MIGRATE:
return _t('Upgrade your encryption');
case PHASE_PASSPHRASE:
return _t('Set up encryption');
case PHASE_PASSPHRASE_CONFIRM:
return _t('Confirm passphrase');
case PHASE_OPTOUT_CONFIRM:
return _t('Warning!');
case PHASE_CONFIRM_SKIP:
return _t('Are you sure?');
case PHASE_SHOWKEY:
return _t('Recovery key');
case PHASE_KEEPITSAFE:
@ -722,9 +723,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
case PHASE_LOADING:
content = this._renderBusyPhase();
break;
case PHASE_RESTORE_KEY_BACKUP:
content = this._renderPhaseRestoreKeyBackup();
break;
case PHASE_MIGRATE:
content = this._renderPhaseMigrate();
break;
@ -746,8 +744,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
case PHASE_DONE:
content = this._renderPhaseDone();
break;
case PHASE_OPTOUT_CONFIRM:
content = this._renderPhaseOptOutConfirm();
case PHASE_CONFIRM_SKIP:
content = this._renderPhaseSkipConfirm();
break;
}
}
@ -763,6 +761,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
title={this._titleForPhase(this.state.phase)}
headerImage={headerImage}
hasCancel={this.props.hasCancel && [PHASE_PASSPHRASE].includes(this.state.phase)}
fixedWidth={false}
>
<div>
{content}

View file

@ -39,7 +39,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
showSummary: PropTypes.bool,
};
defaultProps = {
static defaultProps = {
showSummary: true,
};

View file

@ -1996,7 +1996,7 @@
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.",
"File to import": "File to import",
"Import": "Import",
"Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.": "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.",
"Restore your key backup to upgrade your encryption": "Restore your key backup to upgrade your encryption",
"Restore": "Restore",
"Enter your account password to confirm the upgrade:": "Enter your account password to confirm the upgrade:",
"You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.",
@ -2025,9 +2025,6 @@
"<b>Copy it</b> to your personal cloud storage": "<b>Copy it</b> to your personal cloud storage",
"This device can now verify other devices, granting them access to encrypted messages and marking them as trusted for other users.": "This device can now verify other devices, granting them access to encrypted messages and marking them as trusted for other users.",
"Verify other users in their profile.": "Verify other users in their profile.",
"Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.",
"Set up secret storage": "Set up secret storage",
"Restore your Key Backup": "Restore your Key Backup",
"Upgrade your encryption": "Upgrade your encryption",
"Recovery key": "Recovery key",
"Keep it safe": "Keep it safe",