From c4a5dea3c7cd0b99c7a7eaf4c32606735c3b5d96 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Wed, 18 Sep 2024 17:13:05 +0800 Subject: [PATCH 1/5] Add ONXYKEYS.CREDENTIALS to KEYS_TO_PRESERVE_DELEGATE_ACCESS --- src/libs/actions/Delegate.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index 4797506d1a3c..d2df14348f0e 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -18,7 +18,9 @@ Onyx.connect({ }, }); -const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.SESSION]; +// NOTE: We are preserving the CREDENTIALS because new ones are returned from DelegateConnect and DelegateDisconnect +// and clearing them out would prevent the user from reauthenticating when the restricted authTokens expire. +const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.SESSION, ONYXKEYS.CREDENTIALS]; function connect(email: string) { if (!delegatedAccess?.delegators) { From 220645c5b45229e756669e08f64f5d32a030f570 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 23 Sep 2024 10:00:38 -0700 Subject: [PATCH 2/5] Clear delegate keys and open app after reauthenticating due to expired delegate token --- src/libs/Authentication.ts | 16 ++++++++++++++++ src/libs/actions/Delegate.ts | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/libs/Authentication.ts b/src/libs/Authentication.ts index dd1003591701..89df2b86bb36 100644 --- a/src/libs/Authentication.ts +++ b/src/libs/Authentication.ts @@ -1,6 +1,9 @@ +import Onyx from 'react-native-onyx'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import type Response from '@src/types/onyx/Response'; +import {confirmReadyToOpenApp, openApp} from './actions/App'; +import * as Delegate from './actions/Delegate'; import updateSessionAuthTokens from './actions/Session/updateSessionAuthTokens'; import redirectToSignIn from './actions/SignInRedirect'; import * as ErrorUtils from './ErrorUtils'; @@ -94,6 +97,19 @@ function reauthenticate(command = ''): Promise { // The authentication process is finished so the network can be unpaused to continue processing requests NetworkStore.setIsAuthenticating(false); + + // If we reauthenticated while connected as a delegate, that means we made an API call with an expired delegate token and reauthenticated using the original user's credentials. + // If this happens, clear the delegate keys and restore the user's original account. + // Delegate tokens expire once every 5 days so this shouldn't happen too often. + if (Delegate.isConnectedAsDelegate()) { + console.log('>>>> Reauthenticated while connected as a delegate. Clearing delegate keys and restoring original account.'); + Onyx.clear(Delegate.KEYS_TO_PRESERVE_DELEGATE_ACCESS).then(() => { + debugger; + console.log('>>>> Opening app after clearing delegate keys'); + confirmReadyToOpenApp(); + openApp(); + }); + } }); } diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index 92267c0fece9..eb204098d3d2 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -332,4 +332,18 @@ function removePendingDelegate(email: string) { }); } -export {connect, disconnect, clearDelegatorErrors, addDelegate, requestValidationCode, clearAddDelegateErrors, removePendingDelegate}; +function isConnectedAsDelegate() { + return !!delegatedAccess?.delegate; +} + +export { + connect, + disconnect, + clearDelegatorErrors, + addDelegate, + requestValidationCode, + clearAddDelegateErrors, + removePendingDelegate, + isConnectedAsDelegate, + KEYS_TO_PRESERVE_DELEGATE_ACCESS, +}; From 22ebc5596d810278ed762da839718951f53a5734 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Mon, 23 Sep 2024 11:14:12 -0700 Subject: [PATCH 3/5] When reauthenticating during delegate access, restore the delegate's original account --- src/libs/Authentication.ts | 23 +++++--------- src/libs/actions/Delegate.ts | 31 +++++++++++-------- src/libs/actions/Session/updateSessionUser.ts | 6 ++++ src/types/onyx/Response.ts | 6 ++++ 4 files changed, 38 insertions(+), 28 deletions(-) create mode 100644 src/libs/actions/Session/updateSessionUser.ts diff --git a/src/libs/Authentication.ts b/src/libs/Authentication.ts index 89df2b86bb36..1ab7083b2d8e 100644 --- a/src/libs/Authentication.ts +++ b/src/libs/Authentication.ts @@ -1,8 +1,6 @@ -import Onyx from 'react-native-onyx'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import type Response from '@src/types/onyx/Response'; -import {confirmReadyToOpenApp, openApp} from './actions/App'; import * as Delegate from './actions/Delegate'; import updateSessionAuthTokens from './actions/Session/updateSessionAuthTokens'; import redirectToSignIn from './actions/SignInRedirect'; @@ -87,6 +85,14 @@ function reauthenticate(command = ''): Promise { return; } + // If we reauthenticated due to an expired delegate token, restore the delegate's original account. + // This is because the credentials used to reauthenticate were for the delegate's original account, and not for the account they were connected as. + if (Delegate.isConnectedAsDelegate()) { + Log.info('Reauthenticated while connected as a delegate. Restoring original account.'); + Delegate.restoreDelegateSession(response); + return; + } + // Update authToken in Onyx and in our local variables so that API requests will use the new authToken updateSessionAuthTokens(response.authToken, response.encryptedAuthToken); @@ -97,19 +103,6 @@ function reauthenticate(command = ''): Promise { // The authentication process is finished so the network can be unpaused to continue processing requests NetworkStore.setIsAuthenticating(false); - - // If we reauthenticated while connected as a delegate, that means we made an API call with an expired delegate token and reauthenticated using the original user's credentials. - // If this happens, clear the delegate keys and restore the user's original account. - // Delegate tokens expire once every 5 days so this shouldn't happen too often. - if (Delegate.isConnectedAsDelegate()) { - console.log('>>>> Reauthenticated while connected as a delegate. Clearing delegate keys and restoring original account.'); - Onyx.clear(Delegate.KEYS_TO_PRESERVE_DELEGATE_ACCESS).then(() => { - debugger; - console.log('>>>> Opening app after clearing delegate keys'); - confirmReadyToOpenApp(); - openApp(); - }); - } }); } diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index eb204098d3d2..1515628c1b99 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -10,8 +10,10 @@ import * as SequentialQueue from '@libs/Network/SequentialQueue'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Delegate, DelegatedAccess, DelegateRole} from '@src/types/onyx/Account'; +import type Response from '@src/types/onyx/Response'; import {confirmReadyToOpenApp, openApp} from './App'; import updateSessionAuthTokens from './Session/updateSessionAuthTokens'; +import updateSessionUser from './Session/updateSessionUser'; let delegatedAccess: DelegatedAccess; Onyx.connect({ @@ -320,6 +322,10 @@ function clearAddDelegateErrors(email: string, fieldName: string) { }); } +function isConnectedAsDelegate() { + return !!delegatedAccess?.delegate; +} + function removePendingDelegate(email: string) { if (!delegatedAccess?.delegates) { return; @@ -332,18 +338,17 @@ function removePendingDelegate(email: string) { }); } -function isConnectedAsDelegate() { - return !!delegatedAccess?.delegate; +function restoreDelegateSession(authenticateResponse: Response) { + Onyx.clear(KEYS_TO_PRESERVE_DELEGATE_ACCESS).then(() => { + updateSessionAuthTokens(authenticateResponse?.authToken, authenticateResponse?.encryptedAuthToken); + updateSessionUser(authenticateResponse?.accountID, authenticateResponse?.email); + + NetworkStore.setAuthToken(authenticateResponse.authToken ?? null); + NetworkStore.setIsAuthenticating(false); + + confirmReadyToOpenApp(); + openApp(); + }); } -export { - connect, - disconnect, - clearDelegatorErrors, - addDelegate, - requestValidationCode, - clearAddDelegateErrors, - removePendingDelegate, - isConnectedAsDelegate, - KEYS_TO_PRESERVE_DELEGATE_ACCESS, -}; +export {connect, disconnect, clearDelegatorErrors, addDelegate, requestValidationCode, clearAddDelegateErrors, removePendingDelegate, restoreDelegateSession, isConnectedAsDelegate}; diff --git a/src/libs/actions/Session/updateSessionUser.ts b/src/libs/actions/Session/updateSessionUser.ts new file mode 100644 index 000000000000..75e888469bec --- /dev/null +++ b/src/libs/actions/Session/updateSessionUser.ts @@ -0,0 +1,6 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; + +export default function updateSessionUser(accountID?: number, email?: string) { + Onyx.merge(ONYXKEYS.SESSION, {accountID, email}); +} diff --git a/src/types/onyx/Response.ts b/src/types/onyx/Response.ts index ef558896c55e..3c12611ee841 100644 --- a/src/types/onyx/Response.ts +++ b/src/types/onyx/Response.ts @@ -68,6 +68,12 @@ type Response = { /** Base64 key to decrypt messages from Pusher encrypted channels */ // eslint-disable-next-line @typescript-eslint/naming-convention shared_secret?: string; + + /** The accountID of the user */ + accountID?: number; + + /** The email of the user */ + email?: string; }; export default Response; From 7e4e5383a1fcbc9bbee6a4a845e9a8ee650fe6fc Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Thu, 26 Sep 2024 13:00:27 -0700 Subject: [PATCH 4/5] Prevent isSidebarLoaded key from getting cleared when switching to delegate access --- src/libs/actions/Delegate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index 1515628c1b99..ff44e62829ea 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -30,6 +30,7 @@ const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ ONYXKEYS.SESSION, ONYXKEYS.IS_LOADING_APP, ONYXKEYS.CREDENTIALS, + ONYXKEYS.IS_SIDEBAR_LOADED, ]; function connect(email: string) { From af22fa1bffe48382268d70eb9921c355193adf64 Mon Sep 17 00:00:00 2001 From: Jasper Huang Date: Thu, 26 Sep 2024 13:02:55 -0700 Subject: [PATCH 5/5] add comment --- src/libs/actions/Delegate.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index ff44e62829ea..2d6469dfb8a5 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -30,6 +30,9 @@ const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ ONYXKEYS.SESSION, ONYXKEYS.IS_LOADING_APP, ONYXKEYS.CREDENTIALS, + + // We need to preserve the sidebar loaded state since we never unrender the sidebar when connecting as a delegate + // This allows the report screen to load correctly when the delegate token expires and the delegate is returned to their original account. ONYXKEYS.IS_SIDEBAR_LOADED, ];