diff --git a/lib/components/keyserver-connection-handler.js b/lib/components/keyserver-connection-handler.js --- a/lib/components/keyserver-connection-handler.js +++ b/lib/components/keyserver-connection-handler.js @@ -26,7 +26,7 @@ import type { BaseSocketProps } from '../socket/socket.react.js'; import { logInActionSources, - recoveryActionSources, + type RecoveryActionSource, } from '../types/account-types.js'; import { genericCookieInvalidation } from '../types/session-types.js'; import { authoritativeKeyserverID } from '../utils/authoritative-keyserver.js'; @@ -199,10 +199,10 @@ olmSessionCreator, ]); - const sessionRecoveryInProgress = useSelector( + const activeSessionRecovery = useSelector( state => state.keyserverStore.keyserverInfos[keyserverID]?.connection - .sessionRecoveryInProgress, + .activeSessionRecovery, ); const preRequestUserInfo = useSelector(state => state.currentUserInfo); @@ -231,77 +231,81 @@ const dispatch = useDispatch(); const urlPrefix = useSelector(urlPrefixSelector(keyserverID)); - const performRecovery = React.useCallback(() => { - invariant( - urlPrefix, - `urlPrefix for ${keyserverID} should be set during performRecovery`, - ); - - setAuthInProgress(true); - - let cancelled = false; - const cancel = () => { - cancelled = true; - setAuthInProgress(false); - }; + const performRecovery = React.useCallback( + (recoveryActionSource: RecoveryActionSource) => { + invariant( + urlPrefix, + `urlPrefix for ${keyserverID} should be set during performRecovery`, + ); + + setAuthInProgress(true); + + let cancelled = false; + const cancel = () => { + cancelled = true; + setAuthInProgress(false); + }; + + const promise = (async () => { + const userStateBeforeRecovery = preRequestUserStateRef.current; + try { + const recoverySessionChange = + await resolveKeyserverSessionInvalidation( + dispatch, + cookie, + urlPrefix, + recoveryActionSource, + keyserverID, + ); + if (cancelled) { + // TODO: cancellation won't work because above call handles Redux + // dispatch directly + throw new Error(CANCELLED_ERROR); + } + const sessionChange = + recoverySessionChange ?? genericCookieInvalidation; + if ( + sessionChange.cookieInvalidated || + !sessionChange.cookie || + !sessionChange.cookie.startsWith('user=') + ) { + setNewSession( + dispatch, + sessionChange, + userStateBeforeRecovery, + null, + recoveryActionSource, + keyserverID, + ); + } + } catch (e) { + if (cancelled) { + return; + } + + console.log( + `Error while recovering session with keyserver id ${keyserverID}`, + e, + ); - const promise = (async () => { - const userStateBeforeRecovery = preRequestUserStateRef.current; - try { - const recoverySessionChange = await resolveKeyserverSessionInvalidation( - dispatch, - cookie, - urlPrefix, - recoveryActionSources.cookieInvalidationResolutionAttempt, - keyserverID, - ); - if (cancelled) { - // TODO: cancellation won't work because above call handles Redux - // dispatch directly - throw new Error(CANCELLED_ERROR); - } - const sessionChange = - recoverySessionChange ?? genericCookieInvalidation; - if ( - sessionChange.cookieInvalidated || - !sessionChange.cookie || - !sessionChange.cookie.startsWith('user=') - ) { setNewSession( dispatch, - sessionChange, + genericCookieInvalidation, userStateBeforeRecovery, null, - recoveryActionSources.cookieInvalidationResolutionAttempt, + recoveryActionSource, keyserverID, ); + } finally { + if (!cancelled) { + setAuthInProgress(false); + } } - } catch (e) { - if (cancelled) { - return; - } - - console.log( - `Error while recovering session with keyserver id ${keyserverID}`, - e, - ); - - setNewSession( - dispatch, - genericCookieInvalidation, - userStateBeforeRecovery, - null, - recoveryActionSources.cookieInvalidationResolutionAttempt, - keyserverID, - ); - } finally { - if (!cancelled) { - setAuthInProgress(false); - } - } - })(); - return [promise, cancel]; - }, [dispatch, cookie, urlPrefix, keyserverID]); + })(); + return [promise, cancel]; + }, + [dispatch, cookie, urlPrefix, keyserverID], + ); const cancelPendingAuth = React.useRef void>(null); const prevPerformAuth = React.useRef(performAuth); @@ -312,7 +316,7 @@ const prevPerformRecovery = React.useRef(performRecovery); React.useEffect(() => { - if (sessionRecoveryInProgress && isUserAuthenticated) { + if (activeSessionRecovery && isUserAuthenticated) { cancelPendingAuth.current?.(); cancelPendingAuth.current = null; @@ -323,7 +327,7 @@ } if (!authInProgress) { - const [, cancel] = performRecovery(); + const [, cancel] = performRecovery(activeSessionRecovery); cancelPendingRecovery.current = cancel; } @@ -359,7 +363,7 @@ const [, cancel] = performAuth(); cancelPendingAuth.current = cancel; }, [ - sessionRecoveryInProgress, + activeSessionRecovery, authInProgress, performRecovery, hasAccessToken, diff --git a/lib/keyserver-conn/call-keyserver-endpoint-provider.react.js b/lib/keyserver-conn/call-keyserver-endpoint-provider.react.js --- a/lib/keyserver-conn/call-keyserver-endpoint-provider.react.js +++ b/lib/keyserver-conn/call-keyserver-endpoint-provider.react.js @@ -13,9 +13,13 @@ setNewSession, type SingleKeyserverActionFunc, type ActionFunc, - setSessionRecoveryInProgressActionType, + setActiveSessionRecoveryActionType, } from './keyserver-conn-types.js'; import { canResolveKeyserverSessionInvalidation } from './recovery-utils.js'; +import { + recoveryActionSources, + type RecoveryActionSource, +} from '../types/account-types.js'; import type { PlatformDetails } from '../types/device-types.js'; import type { Endpoint, SocketAPIHandler } from '../types/endpoints.js'; import type { Dispatch } from '../types/redux-types.js'; @@ -75,7 +79,7 @@ +sessionID: ?string, +currentUserInfo: ?CurrentUserInfo, +isSocketConnected: boolean, - +sessionRecoveryInProgress: boolean, + +activeSessionRecovery: ?RecoveryActionSource, +canRecoverSession?: ?boolean, +lastCommunicatedPlatformDetails: ?PlatformDetails, }; @@ -114,7 +118,7 @@ sessionID, currentUserInfo, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, canRecoverSession, lastCommunicatedPlatformDetails, keyserverID, @@ -146,7 +150,7 @@ // just let the caller callSingleKeyserverEndpoint instance continue return Promise.resolve(null); } - if (!sessionRecoveryInProgress) { + if (!activeSessionRecovery) { // Our cookie seems to be valid return Promise.resolve(null); } @@ -191,10 +195,14 @@ keyserverRecoveryAttempts = { waitingCalls: [] }; recoveryAttempts.set(keyserverID, keyserverRecoveryAttempts); } - if (!sessionRecoveryInProgress) { + if (!activeSessionRecovery) { dispatch({ - type: setSessionRecoveryInProgressActionType, - payload: { sessionRecoveryInProgress: true, keyserverID }, + type: setActiveSessionRecoveryActionType, + payload: { + activeSessionRecovery: + recoveryActionSources.cookieInvalidationResolutionAttempt, + keyserverID, + }, }); } const ongoingRecoveryAttempts = keyserverRecoveryAttempts; @@ -246,7 +254,7 @@ (params: ServerCallSelectorParams) => params.sessionID, (params: ServerCallSelectorParams) => params.currentUserInfo, (params: ServerCallSelectorParams) => params.isSocketConnected, - (params: ServerCallSelectorParams) => params.sessionRecoveryInProgress, + (params: ServerCallSelectorParams) => params.activeSessionRecovery, (params: ServerCallSelectorParams) => params.canRecoverSession, (params: ServerCallSelectorParams) => params.lastCommunicatedPlatformDetails, @@ -257,7 +265,7 @@ sessionID: ?string, currentUserInfo: ?CurrentUserInfo, isSocketConnected: boolean, - sessionRecoveryInProgress: boolean, + activeSessionRecovery: ?RecoveryActionSource, canRecoverSession: ?boolean, lastCommunicatedPlatformDetails: ?PlatformDetails, ) => @@ -268,7 +276,7 @@ sessionID, currentUserInfo, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, canRecoverSession, lastCommunicatedPlatformDetails, keyserverID, @@ -317,7 +325,7 @@ ], ); - // SECTION 4: flush waitingCalls when sessionRecoveryInProgress flips to false + // SECTION 4: flush waitingCalls when activeSessionRecovery flips to falsy const prevKeyserverCallInfosRef = React.useRef<{ +[keyserverID: string]: KeyserverCallInfo, @@ -332,8 +340,8 @@ } const keyserverCallInfo = keyserverCallInfos[keyserverID]; if ( - !keyserverCallInfo.sessionRecoveryInProgress && - prevKeyserverCallInfo.sessionRecoveryInProgress + !keyserverCallInfo.activeSessionRecovery && + prevKeyserverCallInfo.activeSessionRecovery ) { sessionRecoveriesConcluded.add(keyserverID); } diff --git a/lib/keyserver-conn/keyserver-call-infos.js b/lib/keyserver-conn/keyserver-call-infos.js --- a/lib/keyserver-conn/keyserver-call-infos.js +++ b/lib/keyserver-conn/keyserver-call-infos.js @@ -3,6 +3,7 @@ import { createSelector } from 'reselect'; import { useDerivedObject } from '../hooks/objects.js'; +import type { RecoveryActionSource } from '../types/account-types.js'; import type { PlatformDetails } from '../types/device-types.js'; import type { KeyserverInfo } from '../types/keyserver-types.js'; @@ -16,7 +17,7 @@ +urlPrefix: string, +sessionID: ?string, +isSocketConnected: boolean, - +sessionRecoveryInProgress: boolean, + +activeSessionRecovery: ?RecoveryActionSource, +lastCommunicatedPlatformDetails: ?PlatformDetails, }; @@ -29,7 +30,7 @@ (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.connection?.status === 'connected', (keyserverInfo: KeyserverInfoPartial) => - !!keyserverInfo.connection?.sessionRecoveryInProgress, + keyserverInfo.connection?.activeSessionRecovery, (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.lastCommunicatedPlatformDetails, ( @@ -37,14 +38,14 @@ urlPrefix: string, sessionID: ?string, isSocketConnected: boolean, - sessionRecoveryInProgress: boolean, + activeSessionRecovery: ?RecoveryActionSource, lastCommunicatedPlatformDetails: ?PlatformDetails, ) => ({ cookie, urlPrefix, sessionID, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, lastCommunicatedPlatformDetails, }), ); diff --git a/lib/keyserver-conn/keyserver-conn-types.js b/lib/keyserver-conn/keyserver-conn-types.js --- a/lib/keyserver-conn/keyserver-conn-types.js +++ b/lib/keyserver-conn/keyserver-conn-types.js @@ -93,7 +93,6 @@ +keyserverID: string, }; -export const setSessionRecoveryInProgressActionType = - 'SET_SESSION_RECOVERY_IN_PROGRESS'; +export const setActiveSessionRecoveryActionType = 'SET_ACTIVE_SESSION_RECOVERY'; export { setNewSessionActionType, setNewSession }; diff --git a/lib/reducers/keyserver-reducer.js b/lib/reducers/keyserver-reducer.js --- a/lib/reducers/keyserver-reducer.js +++ b/lib/reducers/keyserver-reducer.js @@ -30,7 +30,7 @@ setLateResponseActionType, updateKeyserverReachabilityActionType, setConnectionIssueActionType, - setSessionRecoveryInProgressActionType, + setActiveSessionRecoveryActionType, } from '../keyserver-conn/keyserver-conn-types.js'; import { keyserverStoreOpsHandlers, @@ -137,7 +137,7 @@ cookie: null, connection: { ...state.keyserverInfos[keyserverID].connection, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, }, }, }, @@ -171,7 +171,7 @@ cookie: sessionChange.cookie, connection: { ...newKeyserverInfo.connection, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, }, }; } @@ -235,7 +235,7 @@ connection: { ...state.keyserverInfos[keyserverID].connection, connectionIssue: null, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, }, }, }, @@ -485,7 +485,7 @@ connection: { ...oldConnection, connectionIssue: null, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, queuedActivityUpdates: [], lateResponses: [], }, @@ -520,7 +520,7 @@ connection: { ...oldConnection, connectionIssue: null, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, queuedActivityUpdates: [], lateResponses: [], }, @@ -619,8 +619,8 @@ keyserverStore: processStoreOps(state, [operation]), keyserverStoreOperations: [operation], }; - } else if (action.type === setSessionRecoveryInProgressActionType) { - const { sessionRecoveryInProgress, keyserverID } = action.payload; + } else if (action.type === setActiveSessionRecoveryActionType) { + const { activeSessionRecovery, keyserverID } = action.payload; const operation: ReplaceKeyserverOperation = { type: 'replace_keyserver', payload: { @@ -629,7 +629,7 @@ ...state.keyserverInfos[keyserverID], connection: { ...state.keyserverInfos[keyserverID].connection, - sessionRecoveryInProgress, + activeSessionRecovery, }, }, }, diff --git a/lib/selectors/server-calls.js b/lib/selectors/server-calls.js --- a/lib/selectors/server-calls.js +++ b/lib/selectors/server-calls.js @@ -10,6 +10,7 @@ connectionSelector, lastCommunicatedPlatformDetailsSelector, } from './keyserver-selectors.js'; +import type { RecoveryActionSource } from '../types/account-types.js'; import type { PlatformDetails } from '../types/device-types.js'; import type { AppState } from '../types/redux-types.js'; import type { ConnectionInfo } from '../types/socket-types.js'; @@ -21,7 +22,7 @@ +sessionID: ?string, +currentUserInfo: ?CurrentUserInfo, +isSocketConnected: ?boolean, - +sessionRecoveryInProgress: ?boolean, + +activeSessionRecovery: ?RecoveryActionSource, +lastCommunicatedPlatformDetails: ?PlatformDetails, }; @@ -51,7 +52,7 @@ connectionInfo?.status !== undefined ? connectionInfo?.status === 'connected' : undefined, - sessionRecoveryInProgress: connectionInfo?.sessionRecoveryInProgress, + activeSessionRecovery: connectionInfo?.activeSessionRecovery, lastCommunicatedPlatformDetails, }), ); diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -104,6 +104,7 @@ ConnectionIssue, StateSyncFullActionPayload, StateSyncIncrementalActionPayload, + SetActiveSessionRecoveryPayload, } from './socket-types.js'; import { type ClientStore } from './store-ops-types.js'; import type { SubscriptionUpdateResult } from './subscription-types.js'; @@ -1357,13 +1358,13 @@ +type: 'SET_CONNECTION_ISSUE', +payload: { +connectionIssue: ?ConnectionIssue, +keyserverID: string }, } - | { - +type: 'SET_SESSION_RECOVERY_IN_PROGRESS', - +payload: { +sessionRecoveryInProgress: boolean, +keyserverID: string }, - } | { +type: 'ADD_COMMUNITY', +payload: AddCommunityPayload, + } + | { + +type: 'SET_ACTIVE_SESSION_RECOVERY', + +payload: SetActiveSessionRecoveryPayload, }; export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string); diff --git a/lib/types/socket-types.js b/lib/types/socket-types.js --- a/lib/types/socket-types.js +++ b/lib/types/socket-types.js @@ -3,6 +3,10 @@ import invariant from 'invariant'; import t, { type TInterface, type TUnion } from 'tcomb'; +import { + type RecoveryActionSource, + recoveryActionSources, +} from './account-types.js'; import { type ActivityUpdate, activityUpdateValidator, @@ -51,6 +55,7 @@ loggedOutUserInfoValidator, } from './user-types.js'; import { mixedRawThreadInfoValidator } from '../permissions/minimally-encoded-raw-thread-info-validators.js'; +import { values } from '../utils/objects.js'; import { tShape, tNumber, tID } from '../utils/validation-utils.js'; // The types of messages that the client sends across the socket @@ -498,9 +503,9 @@ +lateResponses: $ReadOnlyArray, +unreachable: boolean, +connectionIssue: ?ConnectionIssue, - // When this is flipped to true, a session recovery is attempted + // When this is flipped to truthy, a session recovery is attempted // This can happen when the keyserver invalidates the session - +sessionRecoveryInProgress: boolean, + +activeSessionRecovery: null | RecoveryActionSource, }; export const connectionInfoValidator: TInterface = tShape({ @@ -521,7 +526,7 @@ 'not_logged_in_error', ]), ), - sessionRecoveryInProgress: t.Boolean, + activeSessionRecovery: t.maybe(t.enums.of(values(recoveryActionSources))), }); export const defaultConnectionInfo: ConnectionInfo = { status: 'connecting', @@ -529,7 +534,12 @@ lateResponses: [], unreachable: false, connectionIssue: null, - sessionRecoveryInProgress: false, + activeSessionRecovery: null, +}; + +export type SetActiveSessionRecoveryPayload = { + +activeSessionRecovery: null | RecoveryActionSource, + +keyserverID: string, }; export type OneTimeKeyGenerator = (inc: number) => string; diff --git a/lib/utils/action-utils.js b/lib/utils/action-utils.js --- a/lib/utils/action-utils.js +++ b/lib/utils/action-utils.js @@ -41,14 +41,13 @@ return cachedNonOverridenBoundServerCall; } - const { urlPrefix, isSocketConnected, sessionRecoveryInProgress } = + const { urlPrefix, isSocketConnected, activeSessionRecovery } = serverCallState; invariant( !!urlPrefix && isSocketConnected !== undefined && isSocketConnected !== null && - sessionRecoveryInProgress !== undefined && - sessionRecoveryInProgress !== null, + activeSessionRecovery !== undefined, 'keyserver missing from keyserverStore', ); @@ -56,7 +55,7 @@ ...serverCallState, urlPrefix, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, dispatch, ...paramOverride, }); diff --git a/lib/utils/keyserver-call.js b/lib/utils/keyserver-call.js --- a/lib/utils/keyserver-call.js +++ b/lib/utils/keyserver-call.js @@ -77,7 +77,7 @@ urlPrefix, sessionID, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, lastCommunicatedPlatformDetails, } = keyserverCallInfos[keyserverID]; @@ -89,7 +89,7 @@ urlPrefix, sessionID, isSocketConnected, - sessionRecoveryInProgress, + activeSessionRecovery, lastCommunicatedPlatformDetails, });