From 437b45f8a65decdf6d83f5e61e484f345505f314 Mon Sep 17 00:00:00 2001 From: David Baker Date: Sat, 25 Jan 2020 15:28:06 +0000 Subject: [PATCH] Remember password for e2e bootstrapping Fixes https://github.com/vector-im/riot-web/issues/12046 --- .../CreateSecretStorageDialog.js | 3 ++- src/components/structures/MatrixChat.js | 24 +++++++++++++++---- src/components/structures/auth/E2eSetup.js | 2 ++ src/components/structures/auth/Login.js | 7 +++++- .../structures/auth/Registration.js | 8 ++++++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 92ede334d0..0867cae6f4 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -55,6 +55,7 @@ function selectText(target) { export default class CreateSecretStorageDialog extends React.PureComponent { static propTypes = { hasCancel: PropTypes.bool, + accountPassword: PropTypes.string, }; defaultProps = { @@ -82,7 +83,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { // does the server offer a UI auth flow with just m.login.password // for /keys/device_signing/upload? canUploadKeysWithPasswordOnly: null, - accountPassword: '', + accountPassword: props.accountPassword, accountPasswordCorrect: null, // set if we are 'upgrading' encryption (making an SSSS store from // an existing key backup secret). diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index b4b38d7617..133d74db45 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -256,6 +256,9 @@ export default createReactClass({ // logout page. Lifecycle.loadSession({}); } + + this._accountPassword = null; + this._accountPasswordTimer = null; }, componentDidMount: function() { @@ -352,6 +355,8 @@ export default createReactClass({ window.removeEventListener("focus", this.onFocus); window.removeEventListener('resize', this.handleResize); this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); + + if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); }, componentWillUpdate: function(props, state) { @@ -1729,9 +1734,8 @@ export default createReactClass({ this.showScreen("forgot_password"); }, - onRegisterFlowComplete: function(credentials) { - this.onUserCompletedLoginFlow(); - return this.onRegistered(credentials); + onRegisterFlowComplete: function(credentials, password) { + return this.onUserCompletedLoginFlow(credentials, password); }, // returns a promise which resolves to the new MatrixClient @@ -1822,7 +1826,14 @@ export default createReactClass({ this._loggedInView = ref; }, - async onUserCompletedLoginFlow(credentials) { + async onUserCompletedLoginFlow(credentials, password) { + this._accountPassword = password; + // self-destruct the password after 5mins + if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); + this._accountPasswordTimer = setTimeout(() => { + this._accountPassword = null; + this._accountPasswordTimer = null; + }, 60 * 5 * 1000); // Wait for the client to be logged in (but not started) // which is enough to ask the server about account data. const loggedIn = new Promise(resolve => { @@ -1836,7 +1847,7 @@ export default createReactClass({ }); // Create and start the client in the background - Lifecycle.setLoggedIn(credentials); + const setLoggedInPromise = Lifecycle.setLoggedIn(credentials); await loggedIn; const cli = MatrixClientPeg.get(); @@ -1865,6 +1876,8 @@ export default createReactClass({ } else { this._onLoggedIn(); } + + return setLoggedInPromise; }, // complete security / e2e setup has finished @@ -1896,6 +1909,7 @@ export default createReactClass({ view = ( ); } else if (this.state.view === VIEWS.POST_REGISTRATION) { diff --git a/src/components/structures/auth/E2eSetup.js b/src/components/structures/auth/E2eSetup.js index a5f4ff933b..29b4345761 100644 --- a/src/components/structures/auth/E2eSetup.js +++ b/src/components/structures/auth/E2eSetup.js @@ -22,6 +22,7 @@ import * as sdk from '../../../index'; export default class E2eSetup extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, + accountPassword: PropTypes.string, }; constructor() { @@ -40,6 +41,7 @@ export default class E2eSetup extends React.Component { diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 7bc2dbcbae..c8b2a1ea9c 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -58,6 +58,11 @@ export default createReactClass({ displayName: 'Login', propTypes: { + // Called when the user has logged in. Params: + // - The object returned by the login API + // - The user's password, if applicable, (may be cached in memory for a + // short time so the user is not required to re-enter their password + // for operations like uploading cross-signing keys). onLoggedIn: PropTypes.func.isRequired, // If true, the component will consider itself busy. @@ -181,7 +186,7 @@ export default createReactClass({ username, phoneCountry, phoneNumber, password, ).then((data) => { this.setState({serverIsAlive: true}); // it must be, we logged in. - this.props.onLoggedIn(data); + this.props.onLoggedIn(data, password); }, (error) => { if (this._unmounted) { return; diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index fdf2f51e00..171d3ada26 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -45,7 +45,13 @@ export default createReactClass({ displayName: 'Registration', propTypes: { + // Called when the user has logged in. Params: + // - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken + // - The user's password, if available and applicable (may be cached in memory + // for a short time so the user is not required to re-enter their password + // for operations like uploading cross-signing keys). onLoggedIn: PropTypes.func.isRequired, + clientSecret: PropTypes.string, sessionId: PropTypes.string, makeRegistrationUrl: PropTypes.func.isRequired, @@ -348,7 +354,7 @@ export default createReactClass({ homeserverUrl: this.state.matrixClient.getHomeserverUrl(), identityServerUrl: this.state.matrixClient.getIdentityServerUrl(), accessToken: response.access_token, - }); + }, this.state.formVals.password); this._setupPushers(cli); // we're still busy until we get unmounted: don't show the registration form again