Add password strength meter to backup creation UI

https://github.com/vector-im/riot-meta/issues/227
This commit is contained in:
David Baker 2018-11-23 15:50:23 +00:00
parent 0c6e98548e
commit 075c13a5bd
5 changed files with 205 additions and 39 deletions

View file

@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../../index';
import MatrixClientPeg from '../../../../MatrixClientPeg';
import { scorePassword } from '../../../../utils/PasswordScorer';
import FileSaver from 'file-saver';
@ -30,6 +31,8 @@ const PHASE_BACKINGUP = 4;
const PHASE_DONE = 5;
const PHASE_OPTOUT_CONFIRM = 6;
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
// XXX: copied from ShareDialog: factor out into utils
function selectText(target) {
const range = document.createRange();
@ -52,6 +55,7 @@ export default React.createClass({
passPhraseConfirm: '',
copied: false,
downloaded: false,
zxcvbnResult: null,
};
},
@ -173,6 +177,10 @@ export default React.createClass({
_onPassPhraseChange: function(e) {
this.setState({
passPhrase: e.target.value,
// precompute this and keep it in state: zxcvbn is fast but
// we use it in a couple of different places so so point recomputing
// it unnecessarily.
zxcvbnResult: scorePassword(e.target.value),
});
},
@ -183,17 +191,46 @@ export default React.createClass({
},
_passPhraseIsValid: function() {
return this.state.passPhrase !== '';
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
},
_renderPhasePassPhrase: function() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let strengthMeter;
let helpText;
if (this.state.zxcvbnResult) {
if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) {
helpText = _t("Great! This 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 = suggestions.length > 0 ? <div>
{suggestions}
</div> : null;
helpText = <div>
{this.state.zxcvbnResult.feedback.warning}
{suggestionBlock}
</div>;
}
strengthMeter = <div>
<progress max={PASSWORD_MIN_SCORE} value={this.state.zxcvbnResult.score} />
</div>;
}
return <div>
<p>{_t("Secure your encrypted message history with a Recovery Passphrase.")}</p>
<p>{_t("You'll need it if you log out or lose access to this device.")}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseHelp">
{strengthMeter}
{helpText}
</div>
<input type="password"
onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}