import PropTypes from 'prop-types'; import QRCode from 'qrcode.react'; import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import snackbar from 'soapbox/actions/snackbar'; import { Spinner } from 'soapbox/components/ui'; import { fetchMfa, fetchBackupCodes, setupMfa, confirmMfa, disableMfa, } from '../../actions/mfa'; import { Button, Card, CardBody, CardHeader, CardTitle, Column, Form, FormActions, FormGroup, Input, Stack, Text } from '../../components/ui'; /* Security settings page for user account Routed to /auth/mfa Includes following features: - Set up Multi-factor Auth */ const messages = defineMessages({ heading: { id: 'column.mfa', defaultMessage: 'Multi-Factor Authentication' }, mfa_cancel_button: { id: 'column.mfa_cancel', defaultMessage: 'Cancel' }, mfa_setup_button: { id: 'column.mfa_setup', defaultMessage: 'Proceed to Setup' }, mfa_setup_confirm_button: { id: 'column.mfa_confirm_button', defaultMessage: 'Confirm' }, mfa_setup_disable_button: { id: 'column.mfa_disable_button', defaultMessage: 'Disable' }, passwordFieldLabel: { id: 'security.fields.password.label', defaultMessage: 'Password' }, confirmFail: { id: 'security.confirm.fail', defaultMessage: 'Incorrect code or password. Try again.' }, qrFail: { id: 'security.qr.fail', defaultMessage: 'Failed to fetch setup key' }, codesFail: { id: 'security.codes.fail', defaultMessage: 'Failed to fetch backup codes' }, disableFail: { id: 'security.disable.fail', defaultMessage: 'Incorrect password. Try again.' }, mfaDisableSuccess: { id: 'mfa.disable.success_message', defaultMessage: 'MFA disabled' }, mfaConfirmSuccess: { id: 'mfa.confirm.success_message', defaultMessage: 'MFA confirmed' }, codePlaceholder: { id: 'mfa.mfa_setup.code_placeholder', defaultMessage: 'Code' }, passwordPlaceholder: { id: 'mfa.mfa_setup.password_placeholder', defaultMessage: 'Password' }, }); const mapStateToProps = state => ({ backup_codes: state.getIn(['auth', 'backup_codes', 'codes']), mfa: state.getIn(['security', 'mfa']), }); export default @connect(mapStateToProps) @injectIntl @withRouter class MfaForm extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, mfa: ImmutablePropTypes.map.isRequired, history: PropTypes.object, }; state = { displayOtpForm: false, } handleSetupProceedClick = e => { this.setState({ displayOtpForm: true }); e.preventDefault(); } componentDidMount() { this.props.dispatch(fetchMfa()); } render() { const { intl, mfa } = this.props; const { displayOtpForm } = this.state; return ( {mfa.getIn(['settings', 'totp']) ? ( ) : ( <> {displayOtpForm && } )} ); } } @connect() @injectIntl @withRouter class DisableOtpForm extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, dispatch: PropTypes.func.isRequired, history: PropTypes.object, }; state = { password: '', isLoading: false, } handleInputChange = e => { this.setState({ [e.target.name]: e.target.value }); } handleSubmit = e => { const { password } = this.state; const { dispatch, intl } = this.props; this.setState({ isLoading: true }); dispatch(disableMfa('totp', password)).then(() => { dispatch(snackbar.success(intl.formatMessage(messages.mfaDisableSuccess))); this.props.history.push('../auth/edit'); }).catch(error => { dispatch(snackbar.error(intl.formatMessage(messages.disableFail))); this.setState({ isLoading: false }); }); e.preventDefault(); } render() { const { intl } = this.props; const { isLoading, password } = this.state; return (
} >