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 @@ -34,6 +34,7 @@ }; const AUTH_RETRY_DELAY_MS = 60_000; +const CANCELLED_ERROR = 'cancelled'; function KeyserverConnectionHandler(props: Props) { const { socketComponent: Socket, keyserverID, ...rest } = props; @@ -85,24 +86,25 @@ const olmSessionCreator = React.useContext(OlmSessionCreatorContext); invariant(olmSessionCreator, 'Olm session creator should be set'); - const [canPerformAuth, setCanPerformAuth] = React.useState(true); - const isUserAuthenticated = useSelector(isLoggedInToKeyserver(keyserverID)); + const [authInProgress, setAuthInProgress] = React.useState(false); + const performAuth = React.useCallback(() => { + setAuthInProgress(true); - React.useEffect(() => { - if ( - !usingCommServicesAccessToken || - !canPerformAuth || - isUserAuthenticated - ) { - return; - } - setCanPerformAuth(false); + let cancelled = false; + const cancel = () => { + cancelled = true; + setAuthInProgress(false); + }; - void (async () => { + const promise = (async () => { try { const keyserverKeys = await identityClient.getKeyserverKeys(keyserverID); + if (cancelled) { + throw new Error(CANCELLED_ERROR); + } + const [notifsSession, contentSession] = await Promise.all([ olmSessionCreator.notificationsSessionCreator( cookie, @@ -116,6 +118,10 @@ ), ]); + if (cancelled) { + throw new Error(CANCELLED_ERROR); + } + const { userID, deviceID } = await getAuthMetadata(); invariant(userID, 'userID should be set'); invariant(deviceID, 'deviceID should be set'); @@ -124,54 +130,102 @@ ? { [keyserverID]: { deviceToken } } : {}; + if (cancelled) { + throw new Error(CANCELLED_ERROR); + } + await dispatchActionPromise( keyserverAuthActionTypes, - keyserverAuth({ - userID, - deviceID, - doNotRegister: false, - calendarQuery, - deviceTokenUpdateInput, - logInActionSource: process.env.BROWSER - ? logInActionSources.keyserverAuthFromWeb - : logInActionSources.keyserverAuthFromNative, - keyserverData: { - [keyserverID]: { - initialContentEncryptedMessage: contentSession, - initialNotificationsEncryptedMessage: notifsSession, + (async () => { + await keyserverAuth({ + userID, + deviceID, + doNotRegister: false, + calendarQuery, + deviceTokenUpdateInput, + logInActionSource: process.env.BROWSER + ? logInActionSources.keyserverAuthFromWeb + : logInActionSources.keyserverAuthFromNative, + keyserverData: { + [keyserverID]: { + initialContentEncryptedMessage: contentSession, + initialNotificationsEncryptedMessage: notifsSession, + }, }, - }, - }), + }); + if (cancelled) { + throw new Error(CANCELLED_ERROR); + } + })(), ); } catch (e) { + if (cancelled) { + return; + } + console.log( `Error while authenticating to keyserver with id ${keyserverID}`, e, ); + if (!dataLoaded && keyserverID === ashoatKeyserverID) { await dispatchActionPromise(logOutActionTypes, callLogOut()); } } finally { - await sleep(AUTH_RETRY_DELAY_MS); - setCanPerformAuth(true); + if (!cancelled) { + await sleep(AUTH_RETRY_DELAY_MS); + setAuthInProgress(false); + } } })(); + return [promise, cancel]; }, [ - keyserverID, - identityClient, - olmSessionCreator, - cookie, - getAuthMetadata, - dispatchActionPromise, - keyserverAuth, - deviceToken, calendarQuery, - isUserAuthenticated, callLogOut, + cookie, dataLoaded, - canPerformAuth, + deviceToken, + dispatchActionPromise, + getAuthMetadata, + identityClient, + keyserverAuth, + keyserverID, + olmSessionCreator, ]); + const cancelPendingAuth = React.useRef void>(null); + const prevPerformAuth = React.useRef(performAuth); + const isUserAuthenticated = useSelector(isLoggedInToKeyserver(keyserverID)); + const hasAccessToken = useSelector(state => !!state.commServicesAccessToken); + + React.useEffect(() => { + if (!hasAccessToken) { + cancelPendingAuth.current?.(); + cancelPendingAuth.current = null; + } + + if ( + !usingCommServicesAccessToken || + isUserAuthenticated || + !hasAccessToken + ) { + return; + } + + if (prevPerformAuth.current !== performAuth) { + cancelPendingAuth.current?.(); + cancelPendingAuth.current = null; + } + prevPerformAuth.current = performAuth; + + if (authInProgress) { + return; + } + + const [, cancel] = performAuth(); + cancelPendingAuth.current = cancel; + }, [authInProgress, hasAccessToken, isUserAuthenticated, performAuth]); + if (keyserverID !== ashoatKeyserverID) { return null; }