replace zxcvbn field in CreateKeyBackupDialog with PassphraseField
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
865495dd69
commit
f2979f3fd8
5 changed files with 37 additions and 99 deletions
|
@ -121,7 +121,6 @@
|
||||||
@import "./views/elements/_Tooltip.scss";
|
@import "./views/elements/_Tooltip.scss";
|
||||||
@import "./views/elements/_TooltipButton.scss";
|
@import "./views/elements/_TooltipButton.scss";
|
||||||
@import "./views/elements/_Validation.scss";
|
@import "./views/elements/_Validation.scss";
|
||||||
@import "./views/elements/_ZxcvbnProgressBar.scss";
|
|
||||||
@import "./views/emojipicker/_EmojiPicker.scss";
|
@import "./views/emojipicker/_EmojiPicker.scss";
|
||||||
@import "./views/globals/_MatrixToolbar.scss";
|
@import "./views/globals/_MatrixToolbar.scss";
|
||||||
@import "./views/groups/_GroupPublicityToggle.scss";
|
@import "./views/groups/_GroupPublicityToggle.scss";
|
||||||
|
|
|
@ -35,17 +35,6 @@ limitations under the License.
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseHelp {
|
|
||||||
flex: 1;
|
|
||||||
height: 85px;
|
|
||||||
margin-left: 20px;
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseHelp progress {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseInput {
|
.mx_CreateKeyBackupDialog_passPhraseInput {
|
||||||
flex: none;
|
flex: none;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
|
|
@ -15,18 +15,17 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {createRef} from 'react';
|
||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
import * as sdk from '../../../../index';
|
import * as sdk from '../../../../index';
|
||||||
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { scorePassword } from '../../../../utils/PasswordScorer';
|
import {_t, _td} from '../../../../languageHandler';
|
||||||
import { _t } from '../../../../languageHandler';
|
|
||||||
import { accessSecretStorage } from '../../../../CrossSigningManager';
|
import { accessSecretStorage } from '../../../../CrossSigningManager';
|
||||||
import SettingsStore from '../../../../settings/SettingsStore';
|
import SettingsStore from '../../../../settings/SettingsStore';
|
||||||
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
|
||||||
import {copyNode} from "../../../../utils/strings";
|
import {copyNode} from "../../../../utils/strings";
|
||||||
import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar";
|
import PassphraseField from "../../../../components/views/auth/PassphraseField";
|
||||||
|
|
||||||
const PHASE_PASSPHRASE = 0;
|
const PHASE_PASSPHRASE = 0;
|
||||||
const PHASE_PASSPHRASE_CONFIRM = 1;
|
const PHASE_PASSPHRASE_CONFIRM = 1;
|
||||||
|
@ -37,7 +36,6 @@ const PHASE_DONE = 5;
|
||||||
const PHASE_OPTOUT_CONFIRM = 6;
|
const PHASE_OPTOUT_CONFIRM = 6;
|
||||||
|
|
||||||
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
|
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.
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Walks the user through the process of creating an e2e key backup
|
* Walks the user through the process of creating an e2e key backup
|
||||||
|
@ -53,17 +51,18 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
|
|
||||||
this._recoveryKeyNode = null;
|
this._recoveryKeyNode = null;
|
||||||
this._keyBackupInfo = null;
|
this._keyBackupInfo = null;
|
||||||
this._setZxcvbnResultTimeout = null;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
secureSecretStorage: null,
|
secureSecretStorage: null,
|
||||||
phase: PHASE_PASSPHRASE,
|
phase: PHASE_PASSPHRASE,
|
||||||
passPhrase: '',
|
passPhrase: '',
|
||||||
|
passPhraseValid: false,
|
||||||
passPhraseConfirm: '',
|
passPhraseConfirm: '',
|
||||||
copied: false,
|
copied: false,
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
zxcvbnResult: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this._passphraseField = createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
@ -82,12 +81,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this._setZxcvbnResultTimeout !== null) {
|
|
||||||
clearTimeout(this._setZxcvbnResultTimeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_collectRecoveryKeyNode = (n) => {
|
_collectRecoveryKeyNode = (n) => {
|
||||||
this._recoveryKeyNode = n;
|
this._recoveryKeyNode = n;
|
||||||
}
|
}
|
||||||
|
@ -181,22 +174,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
|
|
||||||
_onPassPhraseNextClick = async (e) => {
|
_onPassPhraseNextClick = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (!this._passphraseField.current) return; // unmounting
|
||||||
|
|
||||||
// If we're waiting for the timeout before updating the result at this point,
|
await this._passphraseField.current.validate({ allowEmpty: false });
|
||||||
// skip ahead and do it now, otherwise we'll deny the attempt to proceed
|
if (!this._passphraseField.current.state.valid) {
|
||||||
// even if the user entered a valid passphrase
|
this._passphraseField.current.focus();
|
||||||
if (this._setZxcvbnResultTimeout !== null) {
|
this._passphraseField.current.validate({ allowEmpty: false, focused: true });
|
||||||
clearTimeout(this._setZxcvbnResultTimeout);
|
return;
|
||||||
this._setZxcvbnResultTimeout = null;
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
this.setState({
|
|
||||||
zxcvbnResult: scorePassword(this.state.passPhrase),
|
|
||||||
}, resolve);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this._passPhraseIsValid()) {
|
|
||||||
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
|
||||||
};
|
};
|
||||||
|
|
||||||
_onPassPhraseConfirmNextClick = async (e) => {
|
_onPassPhraseConfirmNextClick = async (e) => {
|
||||||
|
@ -215,9 +202,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
_onSetAgainClick = () => {
|
_onSetAgainClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
passPhrase: '',
|
passPhrase: '',
|
||||||
|
passPhraseValid: false,
|
||||||
passPhraseConfirm: '',
|
passPhraseConfirm: '',
|
||||||
phase: PHASE_PASSPHRASE,
|
phase: PHASE_PASSPHRASE,
|
||||||
zxcvbnResult: null,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,23 +214,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onPassPhraseValidate = (result) => {
|
||||||
|
this.setState({
|
||||||
|
passPhraseValid: result.valid,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
_onPassPhraseChange = (e) => {
|
_onPassPhraseChange = (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
passPhrase: e.target.value,
|
passPhrase: e.target.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._setZxcvbnResultTimeout !== null) {
|
|
||||||
clearTimeout(this._setZxcvbnResultTimeout);
|
|
||||||
}
|
|
||||||
this._setZxcvbnResultTimeout = setTimeout(() => {
|
|
||||||
this._setZxcvbnResultTimeout = null;
|
|
||||||
this.setState({
|
|
||||||
// precompute this and keep it in state: zxcvbn is fast but
|
|
||||||
// we use it in a couple of different places so no point recomputing
|
|
||||||
// it unnecessarily.
|
|
||||||
zxcvbnResult: scorePassword(this.state.passPhrase),
|
|
||||||
});
|
|
||||||
}, PASSPHRASE_FEEDBACK_DELAY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPassPhraseConfirmChange = (e) => {
|
_onPassPhraseConfirmChange = (e) => {
|
||||||
|
@ -252,35 +232,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_passPhraseIsValid() {
|
|
||||||
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderPhasePassPhrase() {
|
_renderPhasePassPhrase() {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
|
|
||||||
let strengthMeter;
|
|
||||||
let helpText;
|
|
||||||
if (this.state.zxcvbnResult) {
|
|
||||||
if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) {
|
|
||||||
helpText = _t("Great! This recovery passphrase looks strong enough.");
|
|
||||||
} else {
|
|
||||||
const suggestions = [];
|
|
||||||
for (let i = 0; i < this.state.zxcvbnResult.feedback.suggestions.length; ++i) {
|
|
||||||
suggestions.push(<div key={i}>{this.state.zxcvbnResult.feedback.suggestions[i]}</div>);
|
|
||||||
}
|
|
||||||
const suggestionBlock = <div>{suggestions.length > 0 ? suggestions : _t("Keep going...")}</div>;
|
|
||||||
|
|
||||||
helpText = <div>
|
|
||||||
{this.state.zxcvbnResult.feedback.warning}
|
|
||||||
{suggestionBlock}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
strengthMeter = <div>
|
|
||||||
<ZxcvbnProgressBar value={this.state.zxcvbnResult.score} />
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <form onSubmit={this._onPassPhraseNextClick}>
|
return <form onSubmit={this._onPassPhraseNextClick}>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"<b>Warning</b>: You should only set up key backup from a trusted computer.", {},
|
"<b>Warning</b>: You should only set up key backup from a trusted computer.", {},
|
||||||
|
@ -294,17 +248,19 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
|
|
||||||
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
<div className="mx_CreateKeyBackupDialog_primaryContainer">
|
||||||
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
|
||||||
<input type="password"
|
<PassphraseField
|
||||||
onChange={this._onPassPhraseChange}
|
|
||||||
value={this.state.passPhrase}
|
|
||||||
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
className="mx_CreateKeyBackupDialog_passPhraseInput"
|
||||||
placeholder={_t("Enter a recovery passphrase...")}
|
onChange={this._onPassPhraseChange}
|
||||||
|
minScore={PASSWORD_MIN_SCORE}
|
||||||
|
value={this.state.passPhrase}
|
||||||
|
onValidate={this._onPassPhraseValidate}
|
||||||
|
fieldRef={this._passphraseField}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
label={_td("Enter a recovery passphrase")}
|
||||||
|
labelEnterPassword={_td("Enter a recovery passphrase")}
|
||||||
|
labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")}
|
||||||
|
labelAllowedButUnsafe={_td("Great! This recovery passphrase looks strong enough.")}
|
||||||
/>
|
/>
|
||||||
<div className="mx_CreateKeyBackupDialog_passPhraseHelp">
|
|
||||||
{strengthMeter}
|
|
||||||
{helpText}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -312,7 +268,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
primaryButton={_t('Next')}
|
primaryButton={_t('Next')}
|
||||||
onPrimaryButtonClick={this._onPassPhraseNextClick}
|
onPrimaryButtonClick={this._onPassPhraseNextClick}
|
||||||
hasCancel={false}
|
hasCancel={false}
|
||||||
disabled={!this._passPhraseIsValid()}
|
disabled={!this.state.passPhraseValid}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
|
@ -61,7 +61,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
this._recoveryKey = null;
|
this._recoveryKey = null;
|
||||||
this._recoveryKeyNode = null;
|
this._recoveryKeyNode = null;
|
||||||
this._setZxcvbnResultTimeout = null;
|
|
||||||
this._backupKey = null;
|
this._backupKey = null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -100,9 +99,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatusChange);
|
MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatusChange);
|
||||||
if (this._setZxcvbnResultTimeout !== null) {
|
|
||||||
clearTimeout(this._setZxcvbnResultTimeout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchBackupInfo() {
|
async _fetchBackupInfo() {
|
||||||
|
@ -504,7 +500,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
onValidate={this._onPassPhraseValidate}
|
onValidate={this._onPassPhraseValidate}
|
||||||
fieldRef={this._passphraseField}
|
fieldRef={this._passphraseField}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
|
||||||
label={_td("Enter a recovery passphrase")}
|
label={_td("Enter a recovery passphrase")}
|
||||||
labelEnterPassword={_td("Enter a recovery passphrase")}
|
labelEnterPassword={_td("Enter a recovery passphrase")}
|
||||||
labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")}
|
labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")}
|
||||||
|
|
|
@ -1888,6 +1888,10 @@
|
||||||
"Your Modular server": "Your Modular server",
|
"Your Modular server": "Your Modular server",
|
||||||
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.",
|
"Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.",
|
||||||
"Server Name": "Server Name",
|
"Server Name": "Server Name",
|
||||||
|
"Enter password": "Enter password",
|
||||||
|
"Nice, strong password!": "Nice, strong password!",
|
||||||
|
"Password is allowed, but unsafe": "Password is allowed, but unsafe",
|
||||||
|
"Keep going...": "Keep going...",
|
||||||
"The email field must not be blank.": "The email field must not be blank.",
|
"The email field must not be blank.": "The email field must not be blank.",
|
||||||
"The username field must not be blank.": "The username field must not be blank.",
|
"The username field must not be blank.": "The username field must not be blank.",
|
||||||
"The phone number field must not be blank.": "The phone number field must not be blank.",
|
"The phone number field must not be blank.": "The phone number field must not be blank.",
|
||||||
|
@ -1902,10 +1906,6 @@
|
||||||
"Use an email address to recover your account": "Use an email address to recover your account",
|
"Use an email address to recover your account": "Use an email address to recover your account",
|
||||||
"Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)",
|
"Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)",
|
||||||
"Doesn't look like a valid email address": "Doesn't look like a valid email address",
|
"Doesn't look like a valid email address": "Doesn't look like a valid email address",
|
||||||
"Enter password": "Enter password",
|
|
||||||
"Password is allowed, but unsafe": "Password is allowed, but unsafe",
|
|
||||||
"Nice, strong password!": "Nice, strong password!",
|
|
||||||
"Keep going...": "Keep going...",
|
|
||||||
"Passwords don't match": "Passwords don't match",
|
"Passwords don't match": "Passwords don't match",
|
||||||
"Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details",
|
"Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details",
|
||||||
"Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)",
|
"Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)",
|
||||||
|
@ -2199,9 +2199,9 @@
|
||||||
"Restore": "Restore",
|
"Restore": "Restore",
|
||||||
"You'll need to authenticate with the server 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.": "You'll need to authenticate with the server to confirm the upgrade.",
|
||||||
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
|
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
|
||||||
"Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.",
|
|
||||||
"Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:",
|
"Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:",
|
||||||
"Enter a recovery passphrase": "Enter a recovery passphrase",
|
"Enter a recovery passphrase": "Enter a recovery passphrase",
|
||||||
|
"Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.",
|
||||||
"Back up encrypted message keys": "Back up encrypted message keys",
|
"Back up encrypted message keys": "Back up encrypted message keys",
|
||||||
"Set up with a recovery key": "Set up with a recovery key",
|
"Set up with a recovery key": "Set up with a recovery key",
|
||||||
"That matches!": "That matches!",
|
"That matches!": "That matches!",
|
||||||
|
@ -2229,7 +2229,6 @@
|
||||||
"Unable to set up secret storage": "Unable to set up secret storage",
|
"Unable to set up secret storage": "Unable to set up secret storage",
|
||||||
"We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.",
|
"We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.",
|
||||||
"For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.",
|
"For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.",
|
||||||
"Enter a recovery passphrase...": "Enter a recovery passphrase...",
|
|
||||||
"Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.",
|
"Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.",
|
||||||
"Repeat your recovery passphrase...": "Repeat your recovery passphrase...",
|
"Repeat your recovery passphrase...": "Repeat your recovery passphrase...",
|
||||||
"Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",
|
"Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).",
|
||||||
|
|
Loading…
Reference in a new issue