From 9a283fe0755cd6828768f11bf03ce083a35e3988 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 27 Jan 2022 01:51:37 -0300 Subject: [PATCH 01/10] [FIX] Differ to Last Session Authenticated --- app/utils/localAuthentication.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index f4359963330..4b0082e2a48 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -132,7 +132,8 @@ export const localAuthenticate = async (server: string): Promise => { const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); // if last authenticated session is older than configured auto lock time, authentication is required - if (diffToLastSession >= serverRecord.autoLockTime!) { + // check if diffToLastSession is negative to prevent bypass local time, just changing the mobile date or time + if (diffToLastSession >= serverRecord.autoLockTime! || diffToLastSession < 0) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); From 88cab2ca046fd620d3d588abb3b2069397501856 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 28 Jan 2022 12:38:08 -0300 Subject: [PATCH 02/10] Added timesync --- app/lib/rocketchat.js | 11 +++++++++++ app/utils/localAuthentication.ts | 19 ++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index ac923025f35..a2c1e328626 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -169,6 +169,17 @@ const RocketChat = { message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') }) }; }, + async getServerTimeSync(server) { + try { + const response = await RNFetchBlob.fetch('GET', `${server}/_timesync`); + if (response?.data) { + return parseInt(response.data); + } + return null; + } catch { + return null; + } + }, stopListener(listener) { return listener && listener.stop(); }, diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index 4b0082e2a48..a2b3bc3941c 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -1,5 +1,4 @@ import * as LocalAuthentication from 'expo-local-authentication'; -import moment from 'moment'; import RNBootSplash from 'react-native-bootsplash'; import AsyncStorage from '@react-native-community/async-storage'; import { sha256 } from 'js-sha256'; @@ -7,6 +6,7 @@ import { sha256 } from 'js-sha256'; import UserPreferences from '../lib/userPreferences'; import store from '../lib/createStore'; import database from '../lib/database'; +import RocketChat from '../lib/rocketchat'; import { ATTEMPTS_KEY, CHANGE_PASSCODE_EMITTER, @@ -21,6 +21,8 @@ import EventEmitter from './events'; import { isIOS } from './deviceInfo'; export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { + const timesync: number = (await RocketChat.getServerTimeSync(server)) ?? 0; + const serversDB = database.servers; const serversCollection = serversDB.get('servers'); await serversDB.write(async () => { @@ -29,7 +31,7 @@ export const saveLastLocalAuthenticationSession = async (server: string, serverR serverRecord = (await serversCollection.find(server)) as TServerModel; } await serverRecord.update(record => { - record.lastLocalAuthenticatedSession = new Date(); + record.lastLocalAuthenticatedSession = new Date(timesync); }); } catch (e) { // Do nothing @@ -128,12 +130,19 @@ export const localAuthenticate = async (server: string): Promise => { // `checkHasPasscode` results newPasscode = true if a passcode has been set if (!result?.newPasscode) { + // Get time from server + const timesync: number | null = await RocketChat.getServerTimeSync(server); + // diff to last authenticated session - const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); + let diffToLastSession = -1; + if (timesync) { + diffToLastSession = Math.round((timesync - serverRecord?.lastLocalAuthenticatedSession.getTime()) / 1000); + } // if last authenticated session is older than configured auto lock time, authentication is required - // check if diffToLastSession is negative to prevent bypass local time, just changing the mobile date or time - if (diffToLastSession >= serverRecord.autoLockTime! || diffToLastSession < 0) { + // check if diffToLastSession is negative + // check if timesync is truly, if isn't will show always the modal + if (!timesync || diffToLastSession < 0 || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); From 0b71ab8d9fcbb9c36a701ab34021c443becc1dac Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 27 Jan 2022 01:51:37 -0300 Subject: [PATCH 03/10] [FIX] Differ to Last Session Authenticated --- app/utils/localAuthentication.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index 0e72d9c5e34..3b39df8c185 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -119,7 +119,8 @@ export const localAuthenticate = async (server: string): Promise => { const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); // if last authenticated session is older than configured auto lock time, authentication is required - if (diffToLastSession >= serverRecord.autoLockTime!) { + // check if diffToLastSession is negative to prevent bypass local time, just changing the mobile date or time + if (diffToLastSession >= serverRecord.autoLockTime! || diffToLastSession < 0) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); From 3db4e29dde74490d2d6cec69ad108a241095eb44 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 28 Jan 2022 12:38:08 -0300 Subject: [PATCH 04/10] Added timesync --- app/lib/rocketchat.js | 11 +++++++++++ app/utils/localAuthentication.ts | 19 ++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 1361f1832b7..fa84363c6c7 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -169,6 +169,17 @@ const RocketChat = { message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') }) }; }, + async getServerTimeSync(server) { + try { + const response = await RNFetchBlob.fetch('GET', `${server}/_timesync`); + if (response?.data) { + return parseInt(response.data); + } + return null; + } catch { + return null; + } + }, stopListener(listener) { return listener && listener.stop(); }, diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index 3b39df8c185..32c1a27acf0 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -1,5 +1,4 @@ import * as LocalAuthentication from 'expo-local-authentication'; -import moment from 'moment'; import RNBootSplash from 'react-native-bootsplash'; import AsyncStorage from '@react-native-community/async-storage'; import { sha256 } from 'js-sha256'; @@ -7,6 +6,7 @@ import { sha256 } from 'js-sha256'; import UserPreferences from '../lib/userPreferences'; import store from '../lib/createStore'; import database from '../lib/database'; +import RocketChat from '../lib/rocketchat'; import { ATTEMPTS_KEY, BIOMETRY_ENABLED_KEY, @@ -22,6 +22,8 @@ import EventEmitter from './events'; import { isIOS } from './deviceInfo'; export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { + const timesync: number = (await RocketChat.getServerTimeSync(server)) ?? 0; + const serversDB = database.servers; const serversCollection = serversDB.get('servers'); await serversDB.write(async () => { @@ -30,7 +32,7 @@ export const saveLastLocalAuthenticationSession = async (server: string, serverR serverRecord = (await serversCollection.find(server)) as TServerModel; } await serverRecord.update(record => { - record.lastLocalAuthenticatedSession = new Date(); + record.lastLocalAuthenticatedSession = new Date(timesync); }); } catch (e) { // Do nothing @@ -115,12 +117,19 @@ export const localAuthenticate = async (server: string): Promise => { // `checkHasPasscode` results newPasscode = true if a passcode has been set if (!result?.newPasscode) { + // Get time from server + const timesync: number | null = await RocketChat.getServerTimeSync(server); + // diff to last authenticated session - const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); + let diffToLastSession = -1; + if (timesync) { + diffToLastSession = Math.round((timesync - serverRecord?.lastLocalAuthenticatedSession.getTime()) / 1000); + } // if last authenticated session is older than configured auto lock time, authentication is required - // check if diffToLastSession is negative to prevent bypass local time, just changing the mobile date or time - if (diffToLastSession >= serverRecord.autoLockTime! || diffToLastSession < 0) { + // check if diffToLastSession is negative + // check if timesync is truly, if isn't will show always the modal + if (!timesync || diffToLastSession < 0 || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); From dac4194846c693624b7917f171b992834f8bc77c Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Wed, 9 Feb 2022 11:28:45 -0300 Subject: [PATCH 05/10] timesync tweaks --- app/utils/localAuthentication.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index 32c1a27acf0..12e16340b39 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -21,9 +21,11 @@ import { TServerModel } from '../definitions/IServer'; import EventEmitter from './events'; import { isIOS } from './deviceInfo'; -export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { - const timesync: number = (await RocketChat.getServerTimeSync(server)) ?? 0; - +export const saveLastLocalAuthenticationSession = async ( + server: string, + serverRecord?: TServerModel, + timesync?: number | null +): Promise => { const serversDB = database.servers; const serversCollection = serversDB.get('servers'); await serversDB.write(async () => { @@ -31,8 +33,9 @@ export const saveLastLocalAuthenticationSession = async (server: string, serverR if (!serverRecord) { serverRecord = (await serversCollection.find(server)) as TServerModel; } + const time = timesync || 0; await serverRecord.update(record => { - record.lastLocalAuthenticatedSession = new Date(timesync); + record.lastLocalAuthenticatedSession = new Date(time); }); } catch (e) { // Do nothing @@ -105,6 +108,9 @@ export const localAuthenticate = async (server: string): Promise => { // if screen lock is enabled if (serverRecord?.autoLock) { + // Get time from server + const timesync = await RocketChat.getServerTimeSync(server); + // Make sure splash screen has been hidden try { await RNBootSplash.hide(); @@ -117,13 +123,11 @@ export const localAuthenticate = async (server: string): Promise => { // `checkHasPasscode` results newPasscode = true if a passcode has been set if (!result?.newPasscode) { - // Get time from server - const timesync: number | null = await RocketChat.getServerTimeSync(server); - // diff to last authenticated session let diffToLastSession = -1; if (timesync) { - diffToLastSession = Math.round((timesync - serverRecord?.lastLocalAuthenticatedSession.getTime()) / 1000); + const lastLocalTime = serverRecord?.lastLocalAuthenticatedSession || new Date(0); + diffToLastSession = Math.round((timesync - lastLocalTime.getTime()) / 1000); } // if last authenticated session is older than configured auto lock time, authentication is required @@ -151,7 +155,7 @@ export const localAuthenticate = async (server: string): Promise => { } await resetAttempts(); - await saveLastLocalAuthenticationSession(server, serverRecord); + await saveLastLocalAuthenticationSession(server, serverRecord, timesync); } }; From 080e7bc82bcd215fbd3b72ead298e29a960e136e Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 11 Feb 2022 13:33:06 -0300 Subject: [PATCH 06/10] refactor diffLastLocalSession and saveLastLocalAuthentication --- app/utils/localAuthentication.ts | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index 1a167f6f1ce..a1d002eb292 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -2,6 +2,7 @@ import * as LocalAuthentication from 'expo-local-authentication'; import RNBootSplash from 'react-native-bootsplash'; import AsyncStorage from '@react-native-community/async-storage'; import { sha256 } from 'js-sha256'; +import moment from 'moment'; import UserPreferences from '../lib/userPreferences'; import { store } from '../lib/auxStore'; @@ -21,17 +22,16 @@ import { TServerModel } from '../definitions/IServer'; import EventEmitter from './events'; import { isIOS } from './deviceInfo'; -export const saveLastLocalAuthenticationSession = async ( - server: string, - serverRecord?: TServerModel, - timesync?: number | null -): Promise => { +export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { + // We need to get the timesync again because this function is been called when we turn the app to background too + const timesync = await RocketChat.getServerTimeSync(server); + const serversDB = database.servers; const serversCollection = serversDB.get('servers'); await serversDB.write(async () => { try { if (!serverRecord) { - serverRecord = (await serversCollection.find(server)) as TServerModel; + serverRecord = await serversCollection.find(server); } const time = timesync || 0; await serverRecord.update(record => { @@ -124,16 +124,10 @@ export const localAuthenticate = async (server: string): Promise => { // `checkHasPasscode` results newPasscode = true if a passcode has been set if (!result?.newPasscode) { // diff to last authenticated session - let diffToLastSession = -1; - if (timesync) { - const lastLocalTime = serverRecord?.lastLocalAuthenticatedSession || new Date(0); - diffToLastSession = Math.round((timesync - lastLocalTime.getTime()) / 1000); - } + const diffToLastSession = moment(timesync).diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); - // if last authenticated session is older than configured auto lock time, authentication is required - // check if diffToLastSession is negative - // check if timesync is truly, if isn't will show always the modal - if (!timesync || diffToLastSession < 0 || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { + // if can't receive the timesync value from the server or last authenticated session is older than configured auto lock time, authentication is required + if (!timesync || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); @@ -155,7 +149,7 @@ export const localAuthenticate = async (server: string): Promise => { } await resetAttempts(); - await saveLastLocalAuthenticationSession(server, serverRecord, timesync); + await saveLastLocalAuthenticationSession(server, serverRecord); } }; From 6c547768540ce24ccb60cbde5aedaa65b4a53650 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Wed, 16 Feb 2022 19:36:58 -0300 Subject: [PATCH 07/10] did a race --- app/lib/rocketchat/rocketchat.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/lib/rocketchat/rocketchat.js b/app/lib/rocketchat/rocketchat.js index c8f7fb8f67e..06666bdc3fc 100644 --- a/app/lib/rocketchat/rocketchat.js +++ b/app/lib/rocketchat/rocketchat.js @@ -177,7 +177,10 @@ const RocketChat = { }, async getServerTimeSync(server) { try { - const response = await RNFetchBlob.fetch('GET', `${server}/_timesync`); + const response = await Promise.race([ + RNFetchBlob.fetch('GET', `${server}/_timesync`), + new Promise(res => setTimeout(res, 2000)) + ]); if (response?.data) { return parseInt(response.data); } From c1d190c7cf0741ad99dd05bf3e5f8c5392822b73 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:33:32 -0300 Subject: [PATCH 08/10] Update comment in app/utils/localAuthentication.ts Co-authored-by: Diego Mello --- app/utils/localAuthentication.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index a1d002eb292..a17c47568b7 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -126,7 +126,7 @@ export const localAuthenticate = async (server: string): Promise => { // diff to last authenticated session const diffToLastSession = moment(timesync).diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); - // if can't receive the timesync value from the server or last authenticated session is older than configured auto lock time, authentication is required + // if it was not possible to get `timesync` from server or the last authenticated session is older than the configured auto lock time, authentication is required if (!timesync || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); From 1e01a653399a1a6e4694222e66439f8e0aab69f0 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 17 Feb 2022 22:34:41 -0300 Subject: [PATCH 09/10] refactor getServerTimeSync and when use this route --- app/lib/rocketchat/rocketchat.js | 14 ------------- .../rocketchat/services/getServerTimeSync.ts | 20 +++++++++++++++++++ app/utils/localAuthentication.ts | 17 ++++++++++------ 3 files changed, 31 insertions(+), 20 deletions(-) create mode 100644 app/lib/rocketchat/services/getServerTimeSync.ts diff --git a/app/lib/rocketchat/rocketchat.js b/app/lib/rocketchat/rocketchat.js index b1673305a65..eea650e88fe 100644 --- a/app/lib/rocketchat/rocketchat.js +++ b/app/lib/rocketchat/rocketchat.js @@ -164,20 +164,6 @@ const RocketChat = { message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') }) }; }, - async getServerTimeSync(server) { - try { - const response = await Promise.race([ - RNFetchBlob.fetch('GET', `${server}/_timesync`), - new Promise(res => setTimeout(res, 2000)) - ]); - if (response?.data) { - return parseInt(response.data); - } - return null; - } catch { - return null; - } - }, stopListener(listener) { return listener && listener.stop(); }, diff --git a/app/lib/rocketchat/services/getServerTimeSync.ts b/app/lib/rocketchat/services/getServerTimeSync.ts new file mode 100644 index 00000000000..a346c27df97 --- /dev/null +++ b/app/lib/rocketchat/services/getServerTimeSync.ts @@ -0,0 +1,20 @@ +import RNFetchBlob from 'rn-fetch-blob'; + +interface IResponse { + data?: string; +} + +export const getServerTimeSync = async (server: string) => { + try { + const response: IResponse | undefined = await Promise.race([ + RNFetchBlob.fetch('GET', `${server}/_timesync`), + new Promise(res => setTimeout(res, 2000)) + ]); + if (response?.data) { + return parseInt(response.data); + } + return null; + } catch { + return null; + } +}; diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index a17c47568b7..141268b4de7 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import UserPreferences from '../lib/userPreferences'; import { store } from '../lib/auxStore'; import database from '../lib/database'; -import RocketChat from '../lib/rocketchat'; +import { getServerTimeSync } from '../lib/rocketchat/services/getServerTimeSync'; import { ATTEMPTS_KEY, BIOMETRY_ENABLED_KEY, @@ -22,9 +22,14 @@ import { TServerModel } from '../definitions/IServer'; import EventEmitter from './events'; import { isIOS } from './deviceInfo'; -export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { - // We need to get the timesync again because this function is been called when we turn the app to background too - const timesync = await RocketChat.getServerTimeSync(server); +export const saveLastLocalAuthenticationSession = async ( + server: string, + serverRecord?: TServerModel, + timesync?: number | null +): Promise => { + if (!timesync) { + timesync = new Date().getTime(); + } const serversDB = database.servers; const serversCollection = serversDB.get('servers'); @@ -109,7 +114,7 @@ export const localAuthenticate = async (server: string): Promise => { // if screen lock is enabled if (serverRecord?.autoLock) { // Get time from server - const timesync = await RocketChat.getServerTimeSync(server); + const timesync = await getServerTimeSync(server); // Make sure splash screen has been hidden try { @@ -149,7 +154,7 @@ export const localAuthenticate = async (server: string): Promise => { } await resetAttempts(); - await saveLastLocalAuthenticationSession(server, serverRecord); + await saveLastLocalAuthenticationSession(server, serverRecord, timesync); } }; From 330f8f5335bdd5162d1b7b7c17a16967ef227fff Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 17 Feb 2022 22:41:36 -0300 Subject: [PATCH 10/10] tweak --- app/lib/rocketchat/services/getServerTimeSync.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/lib/rocketchat/services/getServerTimeSync.ts b/app/lib/rocketchat/services/getServerTimeSync.ts index a346c27df97..da50c07ed64 100644 --- a/app/lib/rocketchat/services/getServerTimeSync.ts +++ b/app/lib/rocketchat/services/getServerTimeSync.ts @@ -1,12 +1,8 @@ import RNFetchBlob from 'rn-fetch-blob'; -interface IResponse { - data?: string; -} - export const getServerTimeSync = async (server: string) => { try { - const response: IResponse | undefined = await Promise.race([ + const response = await Promise.race([ RNFetchBlob.fetch('GET', `${server}/_timesync`), new Promise(res => setTimeout(res, 2000)) ]);