use forms to wrap password fields so Chrome doesn't go wild and prefill all the things

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2020-01-29 13:24:45 +00:00
parent 45735d5ae3
commit cb0392b78d
3 changed files with 57 additions and 64 deletions

View file

@ -295,31 +295,27 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
}); });
} }
_onPassPhraseNextClick = () => { _onPassPhraseNextClick = async () => {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); // If we're waiting for the timeout before updating the result at this point,
} // skip ahead and do it now, otherwise we'll deny the attempt to proceed
// even if the user entered a valid passphrase
_onPassPhraseKeyPress = async (e) => { if (this._setZxcvbnResultTimeout !== null) {
if (e.key === 'Enter') { clearTimeout(this._setZxcvbnResultTimeout);
// If we're waiting for the timeout before updating the result at this point, this._setZxcvbnResultTimeout = null;
// skip ahead and do it now, otherwise we'll deny the attempt to proceed await new Promise((resolve) => {
// even if the user entered a valid passphrase this.setState({
if (this._setZxcvbnResultTimeout !== null) { zxcvbnResult: scorePassword(this.state.passPhrase),
clearTimeout(this._setZxcvbnResultTimeout); }, resolve);
this._setZxcvbnResultTimeout = null; });
await new Promise((resolve) => {
this.setState({
zxcvbnResult: scorePassword(this.state.passPhrase),
}, resolve);
});
}
if (this._passPhraseIsValid()) {
this._onPassPhraseNextClick();
}
} }
} if (this._passPhraseIsValid()) {
this.setState({phase: PHASE_PASSPHRASE_CONFIRM});
}
};
_onPassPhraseConfirmNextClick = async () => { _onPassPhraseConfirmNextClick = async () => {
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
const [keyInfo, encodedRecoveryKey] = const [keyInfo, encodedRecoveryKey] =
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase); await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
this._keyInfo = keyInfo; this._keyInfo = keyInfo;
@ -332,12 +328,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
}); });
} }
_onPassPhraseConfirmKeyPress = (e) => {
if (e.key === 'Enter' && this.state.passPhrase === this.state.passPhraseConfirm) {
this._onPassPhraseConfirmNextClick();
}
}
_onSetAgainClick = () => { _onSetAgainClick = () => {
this.setState({ this.setState({
passPhrase: '', passPhrase: '',
@ -407,7 +397,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
} else if (this.state.canUploadKeysWithPasswordOnly) { } else if (this.state.canUploadKeysWithPasswordOnly) {
authPrompt = <div> authPrompt = <div>
<div>{_t("Enter your account password to confirm the upgrade:")}</div> <div>{_t("Enter your account password to confirm the upgrade:")}</div>
<div><Field type="password" <div><Field
type="password"
id="mx_CreateSecretStorage_accountPassword" id="mx_CreateSecretStorage_accountPassword"
label={_t("Password")} label={_t("Password")}
value={this.state.accountPassword} value={this.state.accountPassword}
@ -469,7 +460,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
</div>; </div>;
} }
return <div> return <form>
<p>{_t( <p>{_t(
"Set up encryption on this device to allow it to verify other devices, " + "Set up encryption on this device to allow it to verify other devices, " +
"granting them access to encrypted messages and marking them as trusted for other users.", "granting them access to encrypted messages and marking them as trusted for other users.",
@ -480,13 +471,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
)}</p> )}</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer"> <div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field type="password" <Field
type="password"
className="mx_CreateSecretStorageDialog_passPhraseField" className="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this._onPassPhraseChange} onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase} value={this.state.passPhrase}
label={_t("Enter a passphrase")} label={_t("Enter a passphrase")}
autoFocus={true} autoFocus={true}
autoComplete="new-password"
/> />
<div className="mx_CreateSecretStorageDialog_passPhraseHelp"> <div className="mx_CreateSecretStorageDialog_passPhraseHelp">
{strengthMeter} {strengthMeter}
@ -499,10 +491,12 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
onChange={this._onUseKeyBackupChange} value={this.state.useKeyBackup} onChange={this._onUseKeyBackupChange} value={this.state.useKeyBackup}
/> />
<DialogButtons primaryButton={_t('Continue')} <DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this._onPassPhraseNextClick} onPrimaryButtonClick={this._onPassPhraseNextClick}
hasCancel={false} hasCancel={false}
disabled={!this._passPhraseIsValid()} disabled={!this._passPhraseIsValid()}
primaryIsSubmit={true}
> >
<button type="button" <button type="button"
onClick={this._onSkipSetupClick} onClick={this._onSkipSetupClick}
@ -516,7 +510,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
{_t("Set up with a recovery key")} {_t("Set up with a recovery key")}
</AccessibleButton></p> </AccessibleButton></p>
</details> </details>
</div>; </form>;
} }
_renderPhasePassPhraseConfirm() { _renderPhasePassPhraseConfirm() {
@ -549,27 +543,30 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
</div>; </div>;
} }
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div> return <form>
<p>{_t( <p>{_t(
"Enter your passphrase a second time to confirm it.", "Enter your passphrase a second time to confirm it.",
)}</p> )}</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer"> <div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field type="password" <Field
type="password"
id="mx_CreateSecretStorageDialog_passPhraseField" id="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this._onPassPhraseConfirmChange} onChange={this._onPassPhraseConfirmChange}
onKeyPress={this._onPassPhraseConfirmKeyPress}
value={this.state.passPhraseConfirm} value={this.state.passPhraseConfirm}
className="mx_CreateSecretStorageDialog_passPhraseField" className="mx_CreateSecretStorageDialog_passPhraseField"
label={_t("Confirm your passphrase")} label={_t("Confirm your passphrase")}
autoFocus={true} autoFocus={true}
autoComplete="new-password"
/> />
<div className="mx_CreateSecretStorageDialog_passPhraseMatch"> <div className="mx_CreateSecretStorageDialog_passPhraseMatch">
{passPhraseMatch} {passPhraseMatch}
</div> </div>
</div> </div>
<DialogButtons primaryButton={_t('Continue')} <DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this._onPassPhraseConfirmNextClick} onPrimaryButtonClick={this._onPassPhraseConfirmNextClick}
hasCancel={false} hasCancel={false}
primaryIsSubmit={true}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm} disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
> >
<button type="button" <button type="button"
@ -577,7 +574,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
className="danger" className="danger"
>{_t("Skip")}</button> >{_t("Skip")}</button>
</DialogButtons> </DialogButtons>
</div>; </form>;
} }
_renderPhaseShowKey() { _renderPhaseShowKey() {

View file

@ -160,6 +160,7 @@ export default createReactClass({
onKeyDown={ this._onKeyDown } onKeyDown={ this._onKeyDown }
onBlur={this._onBlur} onBlur={this._onBlur}
placeholder={ placeholder } placeholder={ placeholder }
autoComplete="off"
/> />
{ clearButton } { clearButton }
</div> </div>

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2018, 2019 New Vector Ltd Copyright 2018, 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
import { Key } from "../../../../Keyboard";
/* /*
* Access Secure Secret Storage by requesting the user's passphrase. * Access Secure Secret Storage by requesting the user's passphrase.
@ -69,6 +68,8 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
} }
_onPassPhraseNext = async () => { _onPassPhraseNext = async () => {
if (this.state.passPhrase.length <= 0) return;
this.setState({ keyMatches: null }); this.setState({ keyMatches: null });
const input = { passphrase: this.state.passPhrase }; const input = { passphrase: this.state.passPhrase };
const keyMatches = await this.props.checkPrivateKey(input); const keyMatches = await this.props.checkPrivateKey(input);
@ -80,6 +81,8 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
} }
_onRecoveryKeyNext = async () => { _onRecoveryKeyNext = async () => {
if (!this.state.recoveryKeyValid) return;
this.setState({ keyMatches: null }); this.setState({ keyMatches: null });
const input = { recoveryKey: this.state.recoveryKey }; const input = { recoveryKey: this.state.recoveryKey };
const keyMatches = await this.props.checkPrivateKey(input); const keyMatches = await this.props.checkPrivateKey(input);
@ -97,18 +100,6 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
}); });
} }
_onPassPhraseKeyPress = (e) => {
if (e.key === Key.ENTER && this.state.passPhrase.length > 0) {
this._onPassPhraseNext();
}
}
_onRecoveryKeyKeyPress = (e) => {
if (e.key === Key.ENTER && this.state.recoveryKeyValid) {
this._onRecoveryKeyNext();
}
}
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
@ -135,7 +126,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
)} )}
</div>; </div>;
} else { } else {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>; keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
} }
content = <div> content = <div>
@ -149,23 +140,26 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
"identity for verifying other devices by entering your passphrase.", "identity for verifying other devices by entering your passphrase.",
)}</p> )}</p>
<div className="mx_AccessSecretStorageDialog_primaryContainer"> <form className="mx_AccessSecretStorageDialog_primaryContainer">
<input type="password" <input
type="password"
className="mx_AccessSecretStorageDialog_passPhraseInput" className="mx_AccessSecretStorageDialog_passPhraseInput"
onChange={this._onPassPhraseChange} onChange={this._onPassPhraseChange}
onKeyPress={this._onPassPhraseKeyPress}
value={this.state.passPhrase} value={this.state.passPhrase}
autoFocus={true} autoFocus={true}
autoComplete="new-password"
/> />
{keyStatus} {keyStatus}
<DialogButtons primaryButton={_t('Next')} <DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onPassPhraseNext} onPrimaryButtonClick={this._onPassPhraseNext}
hasCancel={true} hasCancel={true}
onCancel={this._onCancel} onCancel={this._onCancel}
focus={false} focus={false}
primaryIsSubmit={true}
primaryDisabled={this.state.passPhrase.length === 0} primaryDisabled={this.state.passPhrase.length === 0}
/> />
</div> </form>
{_t( {_t(
"If you've forgotten your passphrase you can "+ "If you've forgotten your passphrase you can "+
"<button1>use your recovery key</button1> or " + "<button1>use your recovery key</button1> or " +
@ -192,7 +186,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
let keyStatus; let keyStatus;
if (this.state.recoveryKey.length === 0) { if (this.state.recoveryKey.length === 0) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"></div>; keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
} else if (this.state.recoveryKeyValid) { } else if (this.state.recoveryKeyValid) {
keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus"> keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")} {"\uD83D\uDC4D "}{_t("This looks like a valid recovery key!")}
@ -221,22 +215,23 @@ export default class AccessSecretStorageDialog extends React.PureComponent {
"identity for verifying other devices by entering your recovery key.", "identity for verifying other devices by entering your recovery key.",
)}</p> )}</p>
<div className="mx_AccessSecretStorageDialog_primaryContainer"> <form className="mx_AccessSecretStorageDialog_primaryContainer">
<input className="mx_AccessSecretStorageDialog_recoveryKeyInput" <input className="mx_AccessSecretStorageDialog_recoveryKeyInput"
onChange={this._onRecoveryKeyChange} onChange={this._onRecoveryKeyChange}
onKeyPress={this._onRecoveryKeyKeyPress}
value={this.state.recoveryKey} value={this.state.recoveryKey}
autoFocus={true} autoFocus={true}
/> />
{keyStatus} {keyStatus}
<DialogButtons primaryButton={_t('Next')} <DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this._onRecoveryKeyNext} onPrimaryButtonClick={this._onRecoveryKeyNext}
hasCancel={true} hasCancel={true}
onCancel={this._onCancel} onCancel={this._onCancel}
focus={false} focus={false}
primaryIsSubmit={true}
primaryDisabled={!this.state.recoveryKeyValid} primaryDisabled={!this.state.recoveryKeyValid}
/> />
</div> </form>
{_t( {_t(
"If you've forgotten your recovery key you can "+ "If you've forgotten your recovery key you can "+
"<button>set up new recovery options</button>." "<button>set up new recovery options</button>."