From cca2e34c6962ebc64bcc23e617ed5c794aa9c743 Mon Sep 17 00:00:00 2001 From: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:58:15 +0530 Subject: [PATCH 01/11] basic start... --- app/views/SecurityPrivacyView.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx index 7ea8954f1a7..690a63b0f3b 100644 --- a/app/views/SecurityPrivacyView.tsx +++ b/app/views/SecurityPrivacyView.tsx @@ -19,6 +19,7 @@ import { toggleCrashErrorsReport } from '../lib/methods/helpers/log'; import Switch from '../containers/Switch'; +import { getUserSelector } from '../selectors/login'; interface ISecurityPrivacyViewProps { navigation: NativeStackNavigationProp; @@ -30,6 +31,9 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele const [server] = useServer(); const e2eEnabled = useAppSelector(state => state.settings.E2E_Enable); + const user = useAppSelector(state => getUserSelector(state)); + + console.log(user) useEffect(() => { navigation.setOptions({ @@ -67,7 +71,7 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele return ( - + {e2eEnabled ? ( <> @@ -89,6 +93,24 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele + + + + + } + additionalAcessibilityLabel={analyticsEventsState} + /> + + + Date: Sun, 7 Sep 2025 17:22:34 +0000 Subject: [PATCH 02/11] chore: format code with Prettier [skip ci] --- app/views/SecurityPrivacyView.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx index 690a63b0f3b..61c3d0899fa 100644 --- a/app/views/SecurityPrivacyView.tsx +++ b/app/views/SecurityPrivacyView.tsx @@ -31,9 +31,9 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele const [server] = useServer(); const e2eEnabled = useAppSelector(state => state.settings.E2E_Enable); - const user = useAppSelector(state => getUserSelector(state)); + const user = useAppSelector(state => getUserSelector(state)); - console.log(user) + console.log(user); useEffect(() => { navigation.setOptions({ @@ -71,7 +71,7 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele return ( - + {e2eEnabled ? ( <> @@ -93,16 +93,16 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele - - - + + - } From 4bc1441c31c8caf108a2631d21a1b16a9ad17946 Mon Sep 17 00:00:00 2001 From: Rohit Bansal <40559587+Rohit3523@users.noreply.github.com> Date: Mon, 8 Sep 2025 02:09:44 +0530 Subject: [PATCH 03/11] Basic 2fa qr display screen --- app/definitions/rest/v1/index.ts | 4 +- app/definitions/rest/v1/me.ts | 7 + app/lib/services/restApi.ts | 4 + app/stacks/InsideStack.tsx | 2 + app/stacks/MasterDetailStack/types.ts | 1 + app/stacks/types.ts | 1 + app/views/SecurityPrivacyView.tsx | 31 ++++- app/views/TotpView.tsx | 177 ++++++++++++++++++++++++++ package.json | 1 + yarn.lock | 37 +++++- 10 files changed, 255 insertions(+), 10 deletions(-) create mode 100644 app/definitions/rest/v1/me.ts create mode 100644 app/views/TotpView.tsx diff --git a/app/definitions/rest/v1/index.ts b/app/definitions/rest/v1/index.ts index d4dcbba0fa2..fe5f9917d16 100644 --- a/app/definitions/rest/v1/index.ts +++ b/app/definitions/rest/v1/index.ts @@ -21,6 +21,7 @@ import { PushEndpoints } from './push'; import { DirectoryEndpoint } from './directory'; import { AutoTranslateEndpoints } from './autotranslate'; import { ModerationEndpoints } from './moderation'; +import { MeEndpoints } from './me'; export type Endpoints = ChannelsEndpoints & ChatEndpoints & @@ -44,4 +45,5 @@ export type Endpoints = ChannelsEndpoints & PushEndpoints & DirectoryEndpoint & AutoTranslateEndpoints & - ModerationEndpoints; + ModerationEndpoints & + MeEndpoints; diff --git a/app/definitions/rest/v1/me.ts b/app/definitions/rest/v1/me.ts new file mode 100644 index 00000000000..e5d40194465 --- /dev/null +++ b/app/definitions/rest/v1/me.ts @@ -0,0 +1,7 @@ +import type { IUser } from '../../IUser'; + +export type MeEndpoints = { + 'me': { + GET: () => IUser; + }; +}; diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index b455ac2d246..98d2d25ec6b 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -1092,3 +1092,7 @@ export const getSupportedVersionsCloud = (uniqueId?: string, domain?: string) => fetch(`https://releases.rocket.chat/v2/server/supportedVersions?uniqueId=${uniqueId}&domain=${domain}&source=mobile`); export const setUserPassword = (password: string) => sdk.methodCall('setUserPassword', password); + +export const getMe = () => sdk.get('me'); + +export const requestUserTotp = (userid: string) => sdk.methodCall('2fa:enable', { msg: 'method', id: userid, method: '2fa:enable', params: [] }); \ No newline at end of file diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx index 1e4c52fbe9e..ec1ddaf31b8 100644 --- a/app/stacks/InsideStack.tsx +++ b/app/stacks/InsideStack.tsx @@ -90,6 +90,7 @@ import { import { isIOS } from '../lib/methods/helpers'; import { TNavigation } from './stackType'; import AccessibilityAndAppearanceView from '../views/AccessibilityAndAppearanceView'; +import TotpView from '../views/TotpView'; // ChatsStackNavigator const ChatsStack = createNativeStackNavigator(); @@ -196,6 +197,7 @@ const SettingsStackNavigator = () => { component={ScreenLockConfigView} options={ScreenLockConfigView.navigationOptions} /> + ); }; diff --git a/app/stacks/MasterDetailStack/types.ts b/app/stacks/MasterDetailStack/types.ts index c2a2a80ea5e..db4b3705db1 100644 --- a/app/stacks/MasterDetailStack/types.ts +++ b/app/stacks/MasterDetailStack/types.ts @@ -207,6 +207,7 @@ export type ModalStackParamList = { name: string; }; AccessibilityAndAppearanceView: undefined; + TotpView: undefined; }; export type MasterDetailInsideStackParamList = { diff --git a/app/stacks/types.ts b/app/stacks/types.ts index 4634b2457b2..d23b648ad1d 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -215,6 +215,7 @@ export type SettingsStackParamList = { PushTroubleshootView: undefined; GetHelpView: undefined; AccessibilityAndAppearanceView: undefined; + TotpView: undefined; }; export type AdminPanelStackParamList = { diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx index 690a63b0f3b..1fbe67d78d6 100644 --- a/app/views/SecurityPrivacyView.tsx +++ b/app/views/SecurityPrivacyView.tsx @@ -20,6 +20,8 @@ import { } from '../lib/methods/helpers/log'; import Switch from '../containers/Switch'; import { getUserSelector } from '../selectors/login'; +import { getMe } from '../lib/services/restApi'; +import { IUser } from '../definitions'; interface ISecurityPrivacyViewProps { navigation: NativeStackNavigationProp; @@ -33,8 +35,6 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele const e2eEnabled = useAppSelector(state => state.settings.E2E_Enable); const user = useAppSelector(state => getUserSelector(state)); - console.log(user) - useEffect(() => { navigation.setOptions({ title: I18n.t('Security_and_privacy') @@ -55,7 +55,7 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele toggleAnalyticsEventsReport(value); }; - const navigateToScreen = (screen: 'E2EEncryptionSecurityView' | 'ScreenLockConfigView') => { + const navigateToScreen = (screen: 'E2EEncryptionSecurityView' | 'ScreenLockConfigView' | 'TotpView') => { // @ts-ignore logEvent(events[`SP_GO_${screen.replace('View', '').toUpperCase()}`]); navigation.navigate(screen); @@ -68,6 +68,10 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele navigateToScreen('ScreenLockConfigView'); }; + const navigateToTotpView = async () => { + navigateToScreen('TotpView'); + }; + return ( @@ -95,17 +99,30 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele - + { + user.services?.totp?.enabled ? ( + <> + + + + ) : null + } } + right={() => } additionalAcessibilityLabel={analyticsEventsState} /> diff --git a/app/views/TotpView.tsx b/app/views/TotpView.tsx new file mode 100644 index 00000000000..6df0f995392 --- /dev/null +++ b/app/views/TotpView.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from 'react'; +import { useNavigation } from '@react-navigation/native'; +import QRCode from 'react-native-qrcode-svg'; +import { + ActivityIndicator, + Linking, + Pressable, + StyleSheet, + Text, + ToastAndroid, + View +} from 'react-native'; +import Clipboard from '@react-native-clipboard/clipboard'; + +import I18n from '../i18n'; +import SafeAreaView from '../containers/SafeAreaView'; +import { useAppSelector } from '../lib/hooks'; +import { getUserSelector } from '../selectors/login'; +import { requestUserTotp } from '../lib/services/restApi'; +import { useTheme } from '../theme'; +import Button from '../containers/Button'; + +interface State { + isLoading: boolean; + secret: string; + url: string; +} + +function TotpView() { + const navigation = useNavigation(); + const user = useAppSelector(state => getUserSelector(state)); + const { colors } = useTheme(); + const [state, setState] = useState({ + isLoading: true, + secret: '', + url: '' + }); + + useEffect(() => { + navigation.setOptions({ + title: I18n.t('Screen_lock') + }); + + requestUserTotp(user.id).then((res) => { + setState({ + isLoading: false, + secret: res.secret, + url: res.url + }); + }); + }, []); + + if (state.isLoading) { + return ( + + + + {I18n.t('Loading')} + + + ); + } + + return ( + + + + {I18n.t('Two_Factor_Authentication')} + + + + Scan this QR code in Authy, Google Authenticator, or any TOTP app. + + + + + + + + + {state.secret} + +