From 6982d7676a00e37fdf6e53afbce40aa21c24aa13 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 1 Apr 2020 17:32:24 -0300 Subject: [PATCH] [NEW] Two Factor authentication via email (#1961) * First api call working * [NEW] REST API Post wrapper 2FA * [NEW] Send 2FA on Email * [I18n] Add translations * [NEW] Translations & Cancel totp * [CHORE] Totp -> TwoFactor * [NEW] Two Factor by email * [NEW] Tablet Support * [FIX] Text colors * [NEW] Password 2fa * [FIX] Encrypt password on 2FA * [NEW] MethodCall2FA * [FIX] Password fallback * [FIX] Wrap all post/methodCall with 2fa * [FIX] Wrap missed function * few fixes * [FIX] Use new TOTP on Login * [improvement] 2fa methodCall Co-authored-by: Djorkaeff Alexandre --- app/containers/TwoFactor/index.js | 137 +++++++++++++++++++ app/containers/TwoFactor/styles.js | 40 ++++++ app/i18n/locales/en.js | 5 + app/i18n/locales/pt-BR.js | 5 + app/index.js | 2 + app/lib/rocketchat.js | 204 +++++++++++++++++++---------- app/utils/fetch.js | 2 +- app/utils/twoFactor.js | 20 +++ app/views/LoginView.js | 62 +-------- app/views/ProfileView/index.js | 2 +- package.json | 3 +- yarn.lock | 39 ++++++ 12 files changed, 393 insertions(+), 128 deletions(-) create mode 100644 app/containers/TwoFactor/index.js create mode 100644 app/containers/TwoFactor/styles.js create mode 100644 app/utils/twoFactor.js diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js new file mode 100644 index 000000000..9d1d9daab --- /dev/null +++ b/app/containers/TwoFactor/index.js @@ -0,0 +1,137 @@ +import React, { useEffect, useState } from 'react'; +import { View, Text } from 'react-native'; +import _ from 'lodash'; +import PropTypes from 'prop-types'; +import { sha256 } from 'js-sha256'; +import Modal from 'react-native-modal'; +import useDeepCompareEffect from 'use-deep-compare-effect'; + +import TextInput from '../TextInput'; +import I18n from '../../i18n'; +import EventEmitter from '../../utils/events'; +import { withTheme } from '../../theme'; +import { withSplit } from '../../split'; +import { themes } from '../../constants/colors'; +import Button from '../Button'; +import sharedStyles from '../../views/Styles'; +import RocketChat from '../../lib/rocketchat'; +import styles from './styles'; + +export const TWO_FACTOR = 'TWO_FACTOR'; + +const methods = { + totp: { + text: 'Open_your_authentication_app_and_enter_the_code', + keyboardType: 'numeric' + }, + email: { + text: 'Verify_your_email_for_the_code_we_sent', + keyboardType: 'numeric' + }, + password: { + title: 'Please_enter_your_password', + text: 'For_your_security_you_must_enter_your_current_password_to_continue', + secureTextEntry: true, + keyboardType: 'default' + } +}; + +const TwoFactor = React.memo(({ theme, split }) => { + const [visible, setVisible] = useState(false); + const [data, setData] = useState({}); + const [code, setCode] = useState(''); + + const method = methods[data.method]; + const isEmail = data.method === 'email'; + + const sendEmail = () => RocketChat.sendEmailCode(); + + useDeepCompareEffect(() => { + if (!_.isEmpty(data)) { + setVisible(true); + } else { + setVisible(false); + } + }, [data]); + + const showTwoFactor = args => setData(args); + + useEffect(() => { + EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor); + + return () => EventEmitter.removeListener(TWO_FACTOR); + }, []); + + const onCancel = () => { + const { cancel } = data; + if (cancel) { + cancel(); + } + setData({}); + }; + + const onSubmit = () => { + const { submit } = data; + if (submit) { + if (data.method === 'password') { + submit(sha256(code)); + } else { + submit(code); + } + } + setData({}); + }; + + const color = themes[theme].titleText; + return ( + + + + {I18n.t(method?.title || 'Two_Factor_Authentication')} + {I18n.t(method?.text)} + + {isEmail && {I18n.t('Send_me_the_code_again')}} + +