From 5794a0da4db2e806a1379defca15c24db061338f Mon Sep 17 00:00:00 2001 From: Daniel Schalla Date: Fri, 1 Mar 2019 19:01:10 +0100 Subject: [PATCH] [MM-14081] Show MFA Prompt after Server returned error for login request (#2599) * Show MFA Prompt after Server returned error for login request * eslint * Hide MFA error on first login attempt --- app/initial_state.js | 4 ---- app/screens/login/index.js | 6 ++---- app/screens/login/login.js | 31 ++++++++++++++++--------------- app/screens/mfa/mfa.js | 3 ++- app/utils/security.js | 10 ++++++++++ 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/app/initial_state.js b/app/initial_state.js index d6bfb544d..9eb364b38 100644 --- a/app/initial_state.js +++ b/app/initial_state.js @@ -123,10 +123,6 @@ const state = { }, }, users: { - checkMfa: { - status: 'not_started', - error: null, - }, login: { status: 'not_started', error: null, diff --git a/app/screens/login/index.js b/app/screens/login/index.js index 5170cfdbd..3a51f5254 100644 --- a/app/screens/login/index.js +++ b/app/screens/login/index.js @@ -8,17 +8,16 @@ import LoginActions from 'app/actions/views/login'; import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general'; -import {checkMfa, login} from 'mattermost-redux/actions/users'; +import {login} from 'mattermost-redux/actions/users'; import Login from './login.js'; function mapStateToProps(state) { - const {checkMfa: checkMfaRequest, login: loginRequest} = state.requests.users; + const {login: loginRequest} = state.requests.users; const config = getConfig(state); const license = getLicense(state); return { ...state.views.login, - checkMfaRequest, loginRequest, config, license, @@ -30,7 +29,6 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ ...LoginActions, - checkMfa, login, }, dispatch), }; diff --git a/app/screens/login/login.js b/app/screens/login/login.js index 3f5e03efe..27106c5d1 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -26,9 +26,12 @@ import {GlobalStyles} from 'app/styles'; import {preventDoubleTap} from 'app/utils/tap'; import tracker from 'app/utils/time_tracker'; import {t} from 'app/utils/i18n'; +import {setMfaPreflightDone, getMfaPreflightDone} from 'app/utils/security'; import {RequestStatus} from 'mattermost-redux/constants'; +const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error']; + export default class Login extends PureComponent { static propTypes = { navigator: PropTypes.object, @@ -38,14 +41,12 @@ export default class Login extends PureComponent { handlePasswordChanged: PropTypes.func.isRequired, handleSuccessfulLogin: PropTypes.func.isRequired, scheduleExpiredNotification: PropTypes.func.isRequired, - checkMfa: PropTypes.func.isRequired, login: PropTypes.func.isRequired, }).isRequired, config: PropTypes.object.isRequired, license: PropTypes.object.isRequired, loginId: PropTypes.string.isRequired, password: PropTypes.string.isRequired, - checkMfaRequest: PropTypes.object.isRequired, loginRequest: PropTypes.object.isRequired, }; @@ -61,8 +62,9 @@ export default class Login extends PureComponent { }; } - componentWillMount() { + componentDidMount() { Dimensions.addEventListener('change', this.orientationDidChange); + setMfaPreflightDone(false); } componentWillReceiveProps(nextProps) { @@ -182,16 +184,7 @@ export default class Login extends PureComponent { return; } - if (this.props.config.EnableMultifactorAuthentication === 'true') { - const result = await this.props.actions.checkMfa(this.props.loginId); - if (result.data) { - this.goToMfa(); - } else { - this.signIn(); - } - } else { - this.signIn(); - } + this.signIn(); }); }); @@ -205,7 +198,13 @@ export default class Login extends PureComponent { signIn = () => { const {actions, loginId, loginRequest, password} = this.props; if (loginRequest.status !== RequestStatus.STARTED) { - actions.login(loginId.toLowerCase(), password); + actions.login(loginId.toLowerCase(), password).then(this.checkLoginResponse); + } + }; + + checkLoginResponse = (data) => { + if (mfaExpectedErrors.includes(data?.error.server_error_id)) { // eslint-disable-line camelcase + this.goToMfa(); } }; @@ -245,7 +244,6 @@ export default class Login extends PureComponent { getLoginErrorMessage = () => { return ( this.getServerErrorForLogin() || - this.props.checkMfaRequest.error || this.state.error ); }; @@ -259,6 +257,9 @@ export default class Login extends PureComponent { if (!errorId) { return error.message; } + if (mfaExpectedErrors.includes(errorId) && !getMfaPreflightDone()) { + return null; + } if ( errorId === 'store.sql_user.get_for_login.app_error' || errorId === 'ent.ldap.do_login.user_not_registered.app_error' diff --git a/app/screens/mfa/mfa.js b/app/screens/mfa/mfa.js index ec0c363ca..1d7c64b70 100644 --- a/app/screens/mfa/mfa.js +++ b/app/screens/mfa/mfa.js @@ -24,6 +24,7 @@ import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_lo import {GlobalStyles} from 'app/styles'; import {preventDoubleTap} from 'app/utils/tap'; import {t} from 'app/utils/i18n'; +import {setMfaPreflightDone} from 'app/utils/security'; export default class Mfa extends PureComponent { static propTypes = { @@ -97,7 +98,7 @@ export default class Mfa extends PureComponent { }); return; } - + setMfaPreflightDone(true); this.props.actions.login(this.props.loginId, this.props.password, this.state.token); }); diff --git a/app/utils/security.js b/app/utils/security.js index 0041d06c2..58633d9a6 100644 --- a/app/utils/security.js +++ b/app/utils/security.js @@ -5,6 +5,16 @@ import {Client4} from 'mattermost-redux/client'; import CookieManager from 'react-native-cookies'; import urlParse from 'url-parse'; +let mfaPreflightDone = false; + +export function setMfaPreflightDone(state) { + mfaPreflightDone = state; +} + +export function getMfaPreflightDone() { + return mfaPreflightDone; +} + export function setCSRFFromCookie(url) { return new Promise((resolve) => { CookieManager.get(urlParse(url).origin, false).then((res) => {