Allow user to control if they are signed out of all devices when changing password (#8259)
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
ee2ee3c08c
commit
bb4064ff43
5 changed files with 157 additions and 76 deletions
|
@ -41,7 +41,11 @@ enum Phase {
|
|||
}
|
||||
|
||||
interface IProps {
|
||||
onFinished?: ({ didSetEmail: boolean }?) => void;
|
||||
onFinished?: (outcome: {
|
||||
didSetEmail?: boolean;
|
||||
/** Was one or more other devices logged out whilst changing the password */
|
||||
didLogoutOutOtherDevices: boolean;
|
||||
}) => void;
|
||||
onError?: (error: {error: string}) => void;
|
||||
rowClassName?: string;
|
||||
buttonClassName?: string;
|
||||
|
@ -82,48 +86,58 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||
};
|
||||
}
|
||||
|
||||
private onChangePassword(oldPassword: string, newPassword: string): void {
|
||||
private async onChangePassword(oldPassword: string, newPassword: string): Promise<void> {
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
if (!this.props.confirm) {
|
||||
this.changePassword(cli, oldPassword, newPassword);
|
||||
return;
|
||||
// if the server supports it then don't sign user out of all devices
|
||||
const serverSupportsControlOfDevicesLogout = await cli.doesServerSupportLogoutDevices();
|
||||
const userHasOtherDevices = (await cli.getDevices()).devices.length > 1;
|
||||
|
||||
if (userHasOtherDevices && !serverSupportsControlOfDevicesLogout && this.props.confirm) {
|
||||
// warn about logging out all devices
|
||||
const { finished } = Modal.createTrackedDialog<[boolean]>('Change Password', '', QuestionDialog, {
|
||||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
<p>{ _t(
|
||||
'Changing your password on this homeserver will cause all of your other devices to be ' +
|
||||
'signed out. This will delete the message encryption keys stored on them, and may make ' +
|
||||
'encrypted chat history unreadable.',
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
'If you want to retain access to your chat history in encrypted rooms you should first ' +
|
||||
'export your room keys and re-import them afterwards.',
|
||||
) }</p>
|
||||
<p>{ _t(
|
||||
'You can also ask your homeserver admin to upgrade the server to change this behaviour.',
|
||||
) }</p>
|
||||
</div>,
|
||||
button: _t("Continue"),
|
||||
extraButtons: [
|
||||
<button
|
||||
key="exportRoomKeys"
|
||||
className="mx_Dialog_primary"
|
||||
onClick={this.onExportE2eKeysClicked}
|
||||
>
|
||||
{ _t('Export E2E room keys') }
|
||||
</button>,
|
||||
],
|
||||
});
|
||||
|
||||
const [confirmed] = await finished;
|
||||
if (!confirmed) return;
|
||||
}
|
||||
|
||||
Modal.createTrackedDialog('Change Password', '', QuestionDialog, {
|
||||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
{ _t(
|
||||
'Changing password will currently reset any end-to-end encryption keys on all sessions, ' +
|
||||
'making encrypted chat history unreadable, unless you first export your room keys ' +
|
||||
'and re-import them afterwards. ' +
|
||||
'In future this will be improved.',
|
||||
) }
|
||||
{ ' ' }
|
||||
<a href="https://github.com/vector-im/element-web/issues/2671" target="_blank" rel="noreferrer noopener">
|
||||
https://github.com/vector-im/element-web/issues/2671
|
||||
</a>
|
||||
</div>,
|
||||
button: _t("Continue"),
|
||||
extraButtons: [
|
||||
<button
|
||||
key="exportRoomKeys"
|
||||
className="mx_Dialog_primary"
|
||||
onClick={this.onExportE2eKeysClicked}
|
||||
>
|
||||
{ _t('Export E2E room keys') }
|
||||
</button>,
|
||||
],
|
||||
onFinished: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this.changePassword(cli, oldPassword, newPassword);
|
||||
}
|
||||
},
|
||||
});
|
||||
this.changePassword(cli, oldPassword, newPassword, serverSupportsControlOfDevicesLogout, userHasOtherDevices);
|
||||
}
|
||||
|
||||
private changePassword(cli: MatrixClient, oldPassword: string, newPassword: string): void {
|
||||
private changePassword(
|
||||
cli: MatrixClient,
|
||||
oldPassword: string,
|
||||
newPassword: string,
|
||||
serverSupportsControlOfDevicesLogout: boolean,
|
||||
userHasOtherDevices: boolean,
|
||||
): void {
|
||||
const authDict = {
|
||||
type: 'm.login.password',
|
||||
identifier: {
|
||||
|
@ -140,15 +154,21 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||
phase: Phase.Uploading,
|
||||
});
|
||||
|
||||
cli.setPassword(authDict, newPassword).then(() => {
|
||||
const logoutDevices = serverSupportsControlOfDevicesLogout ? false : undefined;
|
||||
|
||||
// undefined or true mean all devices signed out
|
||||
const didLogoutOutOtherDevices = !serverSupportsControlOfDevicesLogout && userHasOtherDevices;
|
||||
|
||||
cli.setPassword(authDict, newPassword, logoutDevices).then(() => {
|
||||
if (this.props.shouldAskForEmail) {
|
||||
return this.optionallySetEmail().then((confirmed) => {
|
||||
this.props.onFinished({
|
||||
didSetEmail: confirmed,
|
||||
didLogoutOutOtherDevices,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.props.onFinished();
|
||||
this.props.onFinished({ didLogoutOutOtherDevices });
|
||||
}
|
||||
}, (err) => {
|
||||
this.props.onError(err);
|
||||
|
@ -279,7 +299,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||
if (err) {
|
||||
this.props.onError(err);
|
||||
} else {
|
||||
this.onChangePassword(oldPassword, newPassword);
|
||||
return this.onChangePassword(oldPassword, newPassword);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -260,14 +260,17 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
|||
});
|
||||
};
|
||||
|
||||
private onPasswordChanged = (): void => {
|
||||
private onPasswordChanged = ({ didLogoutOutOtherDevices }: { didLogoutOutOtherDevices: boolean }): void => {
|
||||
let description = _t("Your password was successfully changed.");
|
||||
if (didLogoutOutOtherDevices) {
|
||||
description += " " + _t(
|
||||
"You will not receive push notifications on other devices until you sign back in to them.",
|
||||
);
|
||||
}
|
||||
// TODO: Figure out a design that doesn't involve replacing the current dialog
|
||||
Modal.createTrackedDialog('Password changed', '', ErrorDialog, {
|
||||
title: _t("Success"),
|
||||
description: _t(
|
||||
"Your password was successfully changed. You will not receive " +
|
||||
"push notifications on other sessions until you log back in to them",
|
||||
) + ".",
|
||||
description,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue