MFA: setupMfa/confirmMfa, throw errors to component, don't be optimistic

This commit is contained in:
Alex Gleason 2022-01-12 11:22:46 -06:00
parent c1cfd2ca2a
commit e94acbc44f
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
3 changed files with 49 additions and 21 deletions

View file

@ -60,10 +60,12 @@ export function confirmMfa(method, code, password) {
return (dispatch, getState) => { return (dispatch, getState) => {
const params = { code, password }; const params = { code, password };
dispatch({ type: MFA_CONFIRM_REQUEST, method, code }); dispatch({ type: MFA_CONFIRM_REQUEST, method, code });
return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(() => { return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(({ data }) => {
dispatch({ type: MFA_CONFIRM_SUCCESS, method, code }); dispatch({ type: MFA_CONFIRM_SUCCESS, method, code });
return data;
}).catch(error => { }).catch(error => {
dispatch({ type: MFA_CONFIRM_FAIL, method, code, error }); dispatch({ type: MFA_CONFIRM_FAIL, method, code, error, skipAlert: true });
throw error;
}); });
}; };
} }
@ -71,10 +73,12 @@ export function confirmMfa(method, code, password) {
export function disableMfa(method, password) { export function disableMfa(method, password) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch({ type: MFA_DISABLE_REQUEST, method }); dispatch({ type: MFA_DISABLE_REQUEST, method });
return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(response => { return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(({ data }) => {
dispatch({ type: MFA_DISABLE_SUCCESS, method }); dispatch({ type: MFA_DISABLE_SUCCESS, method });
return data;
}).catch(error => { }).catch(error => {
dispatch({ type: MFA_DISABLE_FAIL, method }); dispatch({ type: MFA_DISABLE_FAIL, method, skipAlert: true });
throw error;
}); });
}; };
} }

View file

@ -118,31 +118,36 @@ class DisableOtpForm extends ImmutablePureComponent {
state = { state = {
password: '', password: '',
isLoading: false,
} }
handleInputChange = e => { handleInputChange = e => {
this.setState({ [e.target.name]: e.target.value }); this.setState({ [e.target.name]: e.target.value });
} }
handleOtpDisableClick = e => { handleSubmit = e => {
const { password } = this.state; const { password } = this.state;
const { dispatch, intl } = this.props; const { dispatch, intl } = this.props;
this.setState({ isLoading: true });
dispatch(disableMfa('totp', password)).then(() => { dispatch(disableMfa('totp', password)).then(() => {
dispatch(snackbar.success(intl.formatMessage(messages.mfaDisableSuccess))); dispatch(snackbar.success(intl.formatMessage(messages.mfaDisableSuccess)));
this.context.router.history.push('../auth/edit');
}).catch(error => { }).catch(error => {
dispatch(snackbar.error(intl.formatMessage(messages.disableFail))); dispatch(snackbar.error(intl.formatMessage(messages.disableFail)));
this.setState({ isLoading: false });
}); });
this.context.router.history.push('../auth/edit');
e.preventDefault(); e.preventDefault();
} }
render() { render() {
const { intl } = this.props; const { intl } = this.props;
const { isLoading, password } = this.state;
return ( return (
<SimpleForm> <SimpleForm onSubmit={this.handleSubmit} disabled={isLoading}>
<div className='security-settings-panel'> <div className='security-settings-panel'>
<h1 className='security-settings-panel__setup-otp'> <h1 className='security-settings-panel__setup-otp'>
<FormattedMessage id='mfa.otp_enabled_title' defaultMessage='OTP Enabled' /> <FormattedMessage id='mfa.otp_enabled_title' defaultMessage='OTP Enabled' />
@ -150,10 +155,16 @@ class DisableOtpForm extends ImmutablePureComponent {
<div><FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' /></div> <div><FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' /></div>
<div><FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth:' /></div> <div><FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth:' /></div>
<ShowablePassword <ShowablePassword
disabled={isLoading}
name='password' name='password'
onChange={this.handleInputChange} onChange={this.handleInputChange}
value={password}
/>
<Button
disabled={isLoading}
className='button button-primary disable'
text={intl.formatMessage(messages.mfa_setup_disable_button)}
/> />
<Button className='button button-primary disable' text={intl.formatMessage(messages.mfa_setup_disable_button)} onClick={this.handleOtpDisableClick} />
</div> </div>
</SimpleForm> </SimpleForm>
); );
@ -255,7 +266,7 @@ class OtpConfirmForm extends ImmutablePureComponent {
state = { state = {
password: '', password: '',
done: false, isLoading: false,
code: '', code: '',
qrCodeURI: '', qrCodeURI: '',
confirm_key: '', confirm_key: '',
@ -275,26 +286,29 @@ class OtpConfirmForm extends ImmutablePureComponent {
this.setState({ [e.target.name]: e.target.value }); this.setState({ [e.target.name]: e.target.value });
} }
handleOtpConfirmClick = e => { handleSubmit = e => {
const { code, password } = this.state;
const { dispatch, intl } = this.props; const { dispatch, intl } = this.props;
const { code, password } = this.state;
this.setState({ isLoading: true });
dispatch(confirmMfa('totp', code, password)).then(() => { dispatch(confirmMfa('totp', code, password)).then(() => {
dispatch(snackbar.success(intl.formatMessage(messages.mfaConfirmSuccess))); dispatch(snackbar.success(intl.formatMessage(messages.mfaConfirmSuccess)));
this.context.router.history.push('../auth/edit');
}).catch(error => { }).catch(error => {
dispatch(snackbar.error(intl.formatMessage(messages.confirmFail))); dispatch(snackbar.error(intl.formatMessage(messages.confirmFail)));
this.setState({ isLoading: false });
}); });
this.context.router.history.push('../auth/edit');
e.preventDefault(); e.preventDefault();
} }
render() { render() {
const { intl } = this.props; const { intl } = this.props;
const { qrCodeURI, confirm_key } = this.state; const { isLoading, qrCodeURI, confirm_key, password, code } = this.state;
return ( return (
<SimpleForm> <SimpleForm onSubmit={this.handleSubmit} disabled={isLoading}>
<div className='security-settings-panel'> <div className='security-settings-panel'>
<fieldset disabled={false}> <fieldset disabled={false}>
@ -319,19 +333,34 @@ class OtpConfirmForm extends ImmutablePureComponent {
name='code' name='code'
onChange={this.handleInputChange} onChange={this.handleInputChange}
autoComplete='off' autoComplete='off'
value={code}
disabled={isLoading}
/> />
<div><FormattedMessage id='mfa.mfa_setup_enter_password' defaultMessage='Enter your current password to confirm your identity:' /></div> <div><FormattedMessage id='mfa.mfa_setup_enter_password' defaultMessage='Enter your current password to confirm your identity:' /></div>
<ShowablePassword <ShowablePassword
name='password' name='password'
onChange={this.handleInputChange} onChange={this.handleInputChange}
value={password}
disabled={isLoading}
/> />
</div> </div>
</FieldsGroup> </FieldsGroup>
</fieldset> </fieldset>
<div className='security-settings-panel__setup-otp__buttons'> <div className='security-settings-panel__setup-otp__buttons'>
<Button className='button button-secondary cancel' text={intl.formatMessage(messages.mfa_cancel_button)} onClick={this.handleCancelClick} /> <Button
<Button className='button button-primary setup' text={intl.formatMessage(messages.mfa_setup_confirm_button)} onClick={this.handleOtpConfirmClick} /> type='button'
className='button button-secondary cancel'
text={intl.formatMessage(messages.mfa_cancel_button)}
onClick={this.handleCancelClick}
disabled={isLoading}
/>
<Button
type='submit'
className='button button-primary setup'
text={intl.formatMessage(messages.mfa_setup_confirm_button)}
disabled={isLoading}
/>
</div> </div>
</div> </div>
</SimpleForm> </SimpleForm>

View file

@ -3,9 +3,7 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
import { import {
MFA_FETCH_SUCCESS, MFA_FETCH_SUCCESS,
MFA_CONFIRM_SUCCESS, MFA_CONFIRM_SUCCESS,
MFA_DISABLE_REQUEST,
MFA_DISABLE_SUCCESS, MFA_DISABLE_SUCCESS,
MFA_DISABLE_FAIL,
} from '../actions/mfa'; } from '../actions/mfa';
import { import {
FETCH_TOKENS_SUCCESS, FETCH_TOKENS_SUCCESS,
@ -49,11 +47,8 @@ export default function security(state = initialState, action) {
return importMfa(state, fromJS(action.data)); return importMfa(state, fromJS(action.data));
case MFA_CONFIRM_SUCCESS: case MFA_CONFIRM_SUCCESS:
return enableMfa(state, action.method); return enableMfa(state, action.method);
case MFA_DISABLE_REQUEST:
case MFA_DISABLE_SUCCESS: case MFA_DISABLE_SUCCESS:
return disableMfa(state, action.method); return disableMfa(state, action.method);
case MFA_DISABLE_FAIL:
return enableMfa(state, action.method);
default: default:
return state; return state;
} }