diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index 4c14f356e4..3889f0989a 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -1,6 +1,6 @@ /* Copyright 2016 OpenMarket 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"); you may not use this file except in compliance with the License. @@ -23,71 +23,109 @@ import Analytics from '../../../Analytics'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import * as Lifecycle from '../../../Lifecycle'; import { _t } from '../../../languageHandler'; +import InteractiveAuth, {ERROR_USER_CANCELLED} from "../../structures/InteractiveAuth"; +import {DEFAULT_PHASE, PasswordAuthEntry, SSOAuthEntry} from "../auth/InteractiveAuthEntryComponents"; + +const dialogAesthetics = { + [SSOAuthEntry.PHASE_PREAUTH]: { + body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."), + continueText: _t("Single Sign On"), + continueKind: "danger", + }, + [SSOAuthEntry.PHASE_POSTAUTH]: { + body: _t("Are you sure you want to deactivate your account? This is irreversible."), + continueText: _t("Confirm account deactivation"), + continueKind: "danger", + }, +}; + +// This is the same as aestheticsForStagePhases in InteractiveAuthDialog minus the `title` +const DEACTIVATE_AESTHETICS = { + [SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics, + [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics, + [PasswordAuthEntry.LOGIN_TYPE]: { + [DEFAULT_PHASE]: { + body: _t("To continue, please enter your password:"), + }, + }, +}; export default class DeactivateAccountDialog extends React.Component { constructor(props) { super(props); - this._onOk = this._onOk.bind(this); - this._onCancel = this._onCancel.bind(this); - this._onPasswordFieldChange = this._onPasswordFieldChange.bind(this); - this._onEraseFieldChange = this._onEraseFieldChange.bind(this); - this.state = { - password: "", - busy: false, shouldErase: false, errStr: null, + authData: null, // for UIA + + // A few strings that are passed to InteractiveAuth for design or are displayed + // next to the InteractiveAuth component. + bodyText: null, + continueText: null, + continueKind: null, }; - } - _onPasswordFieldChange(ev) { - this.setState({ - password: ev.target.value, - }); - } - - _onEraseFieldChange(ev) { - this.setState({ - shouldErase: ev.target.checked, - }); - } - - async _onOk() { - this.setState({busy: true}); - - try { - // This assumes that the HS requires password UI auth - // for this endpoint. In reality it could be any UI auth. - const auth = { - type: 'm.login.password', - // TODO: Remove `user` once servers support proper UIA - // See https://github.com/vector-im/riot-web/issues/10312 - user: MatrixClientPeg.get().credentials.userId, - identifier: { - type: "m.id.user", - user: MatrixClientPeg.get().credentials.userId, - }, - password: this.state.password, - }; - await MatrixClientPeg.get().deactivateAccount(auth, this.state.shouldErase); - } catch (err) { - let errStr = _t('Unknown error'); - // https://matrix.org/jira/browse/SYN-744 - if (err.httpStatus === 401 || err.httpStatus === 403) { - errStr = _t('Incorrect password'); + MatrixClientPeg.get().deactivateAccount(null, false).then(r => { + // If we got here, oops. The server didn't require any auth. + // Our application lifecycle will catch the error and do the logout bits. + // We'll try to log something in an vain attempt to record what happened (storage + // is also obliterated on logout). + console.warn("User's account got deactivated without confirmation: Server had no auth"); + this.setState({errStr: _t("Server did not require any authentication")}); + }).catch(e => { + if (e && e.httpStatus === 401 && e.data) { + // Valid UIA response + this.setState({authData: e.data}); + } else { + this.setState({errStr: _t("Server did not return valid authentication information.")}); } - this.setState({ - busy: false, - errStr: errStr, - }); + }); + } + + _onStagePhaseChange = (stage, phase) => { + const aesthetics = DEACTIVATE_AESTHETICS[stage]; + let bodyText = null; + let continueText = null; + let continueKind = null; + if (aesthetics) { + const phaseAesthetics = aesthetics[phase]; + if (phaseAesthetics && phaseAesthetics.body) bodyText = phaseAesthetics.body; + if (phaseAesthetics && phaseAesthetics.continueText) continueText = phaseAesthetics.continueText; + if (phaseAesthetics && phaseAesthetics.continueKind) continueKind = phaseAesthetics.continueKind; + } + this.setState({bodyText, continueText, continueKind}); + }; + + _onUIAuthFinished = (success, result, extra) => { + if (success) return; // great! makeRequest() will be called too. + + if (result === ERROR_USER_CANCELLED) { + this._onCancel(); return; } - Analytics.trackEvent('Account', 'Deactivate Account'); - Lifecycle.onLoggedOut(); - this.props.onFinished(true); - } + console.error("Error during UI Auth:", {result, extra}); + this.setState({errStr: _t("There was a problem communicating with the server. Please try again.")}); + }; + + _onUIAuthComplete = (auth) => { + MatrixClientPeg.get().deactivateAccount(auth, this.state.shouldErase).then(r => { + // Deactivation worked - logout & close this dialog + Analytics.trackEvent('Account', 'Deactivate Account'); + Lifecycle.onLoggedOut(); + this.props.onFinished(true); + }).catch(e => { + console.error(e); + this.setState({errStr: _t("There was a problem communicating with the server. Please try again.")}); + }); + }; + + _onEraseFieldChange = (ev) => { + this.setState({ + shouldErase: ev.target.checked, + }); + }; _onCancel() { this.props.onFinished(false); @@ -95,34 +133,36 @@ export default class DeactivateAccountDialog extends React.Component { render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const Loader = sdk.getComponent("elements.Spinner"); - let passwordBoxClass = ''; let error = null; if (this.state.errStr) { error =
{ _t("To continue, please enter your password:") }
-