diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -22,7 +22,10 @@ import { fetchLatestDeviceList } from '../shared/device-list-utils.js'; import { IdentityClientContext } from '../shared/identity-client-context.js'; import threadWatcher from '../shared/thread-watcher.js'; -import { permissionsAndAuthRelatedRequestTimeout } from '../shared/timeouts.js'; +import { + permissionsAndAuthRelatedRequestTimeout, + callIdentityServiceTimeout, +} from '../shared/timeouts.js'; import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import type { LegacyLogInInfo, @@ -451,14 +454,9 @@ ); } -// Unlike useDeleteAccount, we always dispatch a success here (never throw). -// That's because useDeleteAccount is used in a scenario where the user is -// visibly logged-in, and we don't want to log them out unless we succeeded in -// deleting their account. On the other hand, useDeleteDiscardedIdentityAccount -// is used in a scenario where the user is visibly logged-out, and it's only -// used to reset state (eg. Redux, SQLite) to a logged-out state. The state -// reset only occurs when a success action is dispatched, so we always dispatch -// a success. +// useDeleteDiscardedIdentityAccount is used in a scenario where the user is +// visibly logged-out, and it's only used to reset state (eg. Redux, SQLite) to +// a logged-out state. function useDeleteDiscardedIdentityAccount(): ( password: ?string, ) => Promise { @@ -489,15 +487,15 @@ const deleteUserPromise = password ? identityClient.deletePasswordUser(password) : identityClient.deleteWalletUser(); - try { - await Promise.race([ - deleteUserPromise, - (async () => { - await sleep(500); - throw new Error('identity delete user call took more than 500ms'); - })(), - ]); - } catch {} + + await Promise.race([ + deleteUserPromise, + (async () => { + await sleep(callIdentityServiceTimeout); + throw new Error('identity delete user call took more than 500ms'); + })(), + ]); + return { currentUserInfo: null, preRequestUserState: { diff --git a/lib/shared/timeouts.js b/lib/shared/timeouts.js --- a/lib/shared/timeouts.js +++ b/lib/shared/timeouts.js @@ -52,3 +52,6 @@ // This timeout is used for all the requests that perform expensive operations // related to permissions and auth, e.g. change role, etc. export const permissionsAndAuthRelatedRequestTimeout = 60000; + +// Client-side timeout duration for certain identity service RPCs. +export const callIdentityServiceTimeout = 5000; diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js --- a/native/account/registration/registration-server-call.js +++ b/native/account/registration/registration-server-call.js @@ -14,10 +14,12 @@ } from 'lib/actions/user-actions.js'; import { useKeyserverAuthWithRetry } from 'lib/keyserver-conn/keyserver-auth.js'; import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js'; +import { usePreRequestUserState } from 'lib/selectors/account-selectors.js'; import { isLoggedInToKeyserver } from 'lib/selectors/user-selectors.js'; import { type LegacyLogInStartingPayload, logInActionSources, + type LogOutResult, } from 'lib/types/account-types.js'; import { syncedMetadataNames } from 'lib/types/synced-metadata-types.js'; import { getMessageForException } from 'lib/utils/errors.js'; @@ -422,6 +424,10 @@ // We call deleteDiscardedIdentityAccount in order to reset state if identity // registration succeeds but authoritative keyserver auth fails const deleteDiscardedIdentityAccount = useDeleteDiscardedIdentityAccount(); + const preRequestUserState = usePreRequestUserState(); + const commServicesAccessToken = useSelector( + state => state.commServicesAccessToken, + ); const registeringOnAuthoritativeKeyserverRef = React.useRef(false); React.useEffect(() => { @@ -463,39 +469,53 @@ const messageForException = getMessageForException( keyserverAuthException, ); - const discardIdentityAccountPromise = (async () => { - try { - const deletionResult = await deleteDiscardedIdentityAccount( - credentialsToSave?.password, - ); - if (messageForException === 'client_version_unsupported') { - Alert.alert( - appOutOfDateAlertDetails.title, - appOutOfDateAlertDetails.message, - [{ text: 'OK', onPress: onAlertAcknowledged }], - { cancelable: !onAlertAcknowledged }, + const discardIdentityAccountPromise: Promise = + (async () => { + try { + const deletionResult = await deleteDiscardedIdentityAccount( + credentialsToSave?.password, ); - } else { + if (messageForException === 'client_version_unsupported') { + Alert.alert( + appOutOfDateAlertDetails.title, + appOutOfDateAlertDetails.message, + [{ text: 'OK', onPress: onAlertAcknowledged }], + { cancelable: !onAlertAcknowledged }, + ); + } else { + Alert.alert( + unknownErrorAlertDetails.title, + unknownErrorAlertDetails.message, + [{ text: 'OK', onPress: onAlertAcknowledged }], + { cancelable: !onAlertAcknowledged }, + ); + } + return deletionResult; + } catch (deleteException) { + // We swallow the exception here because + // discardIdentityAccountPromise is used in a scenario where the + // user is visibly logged-out, and it's only used to reset state + // (eg. Redux, SQLite) to a logged-out state. The state reset + // only occurs when a success action is dispatched, so by + // swallowing exceptions we ensure that we always dispatch a + // success. Alert.alert( - unknownErrorAlertDetails.title, - unknownErrorAlertDetails.message, + 'Account created but login failed', + 'We were able to create your account, but were unable to log ' + + 'you in. Try going back to the login screen and logging in ' + + 'with your new credentials.', [{ text: 'OK', onPress: onAlertAcknowledged }], { cancelable: !onAlertAcknowledged }, ); + return { + currentUserInfo: null, + preRequestUserState: { + ...preRequestUserState, + commServicesAccessToken, + }, + }; } - return deletionResult; - } catch (deleteException) { - Alert.alert( - 'Account created but login failed', - 'We were able to create your account, but were unable to log ' + - 'you in. Try going back to the login screen and logging in ' + - 'with your new credentials.', - [{ text: 'OK', onPress: onAlertAcknowledged }], - { cancelable: !onAlertAcknowledged }, - ); - throw deleteException; - } - })(); + })(); void dispatchActionPromise( deleteAccountActionTypes, discardIdentityAccountPromise, @@ -513,6 +533,8 @@ keyserverAuth, dispatchActionPromise, deleteDiscardedIdentityAccount, + preRequestUserState, + commServicesAccessToken, ]); // STEP 3: SETTING AVATAR