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 @@ -15,6 +15,9 @@ ClaimUsernameResponse, LogInResponse, LogInRequest, + KeyserverAuthResult, + KeyserverAuthInfo, + KeyserverAuthRequest, } from '../types/account-types.js'; import type { UpdateUserAvatarRequest, @@ -223,6 +226,121 @@ }; }; +const keyserverAuthActionTypes = Object.freeze({ + started: 'KEYSERVER_AUTH_STARTED', + success: 'KEYSERVER_AUTH_SUCCESS', + failed: 'KEYSERVER_AUTH_FAILED', +}); +const keyserverAuthCallServerEndpointOptions = { timeout: 60000 }; +const keyserverAuth = + ( + callKeyserverEndpoint: CallKeyserverEndpoint, + ): ((input: KeyserverAuthInfo) => Promise) => + async keyserverAuthInfo => { + const watchedIDs = threadWatcher.getWatchedIDs(); + + const { + logInActionSource, + calendarQuery, + keyserverData, + deviceTokenUpdateInput, + ...restLogInInfo + } = keyserverAuthInfo; + + const keyserverIDs = Object.keys(keyserverData); + + const watchedIDsPerKeyserver = sortThreadIDsPerKeyserver(watchedIDs); + const calendarQueryPerKeyserver = sortCalendarQueryPerKeyserver( + calendarQuery, + keyserverIDs, + ); + + const requests: { [string]: KeyserverAuthRequest } = {}; + for (const keyserverID of keyserverIDs) { + requests[keyserverID] = { + ...restLogInInfo, + deviceTokenUpdateRequest: deviceTokenUpdateInput[keyserverID], + watchedIDs: watchedIDsPerKeyserver[keyserverID] ?? [], + calendarQuery: calendarQueryPerKeyserver[keyserverID], + platformDetails: getConfig().platformDetails, + initialContentEncryptedMessage: + keyserverData[keyserverID].initialContentEncryptedMessage, + initialNotificationsEncryptedMessage: + keyserverData[keyserverID].initialNotificationsEncryptedMessage, + source: logInActionSource, + }; + } + + const responses: { +[string]: LogInResponse } = await callKeyserverEndpoint( + 'keyserver_auth', + requests, + keyserverAuthCallServerEndpointOptions, + ); + + const userInfosArrays = []; + + let threadInfos: RawThreadInfos = {}; + const calendarResult: WritableCalendarResult = { + calendarQuery: keyserverAuthInfo.calendarQuery, + rawEntryInfos: [], + }; + const messagesResult: WritableGenericMessagesResult = { + messageInfos: [], + truncationStatus: {}, + watchedIDsAtRequestTime: watchedIDs, + currentAsOf: {}, + }; + let updatesCurrentAsOf: { +[string]: number } = {}; + for (const keyserverID in responses) { + threadInfos = { + ...responses[keyserverID].cookieChange.threadInfos, + ...threadInfos, + }; + if (responses[keyserverID].rawEntryInfos) { + calendarResult.rawEntryInfos = calendarResult.rawEntryInfos.concat( + responses[keyserverID].rawEntryInfos, + ); + } + messagesResult.messageInfos = messagesResult.messageInfos.concat( + responses[keyserverID].rawMessageInfos, + ); + messagesResult.truncationStatus = { + ...messagesResult.truncationStatus, + ...responses[keyserverID].truncationStatuses, + }; + messagesResult.currentAsOf = { + ...messagesResult.currentAsOf, + [keyserverID]: responses[keyserverID].serverTime, + }; + updatesCurrentAsOf = { + ...updatesCurrentAsOf, + [keyserverID]: responses[keyserverID].serverTime, + }; + userInfosArrays.push(responses[keyserverID].userInfos); + userInfosArrays.push(responses[keyserverID].cookieChange.userInfos); + } + + const userInfos = mergeUserInfos(...userInfosArrays); + + return { + threadInfos, + currentUserInfo: responses[ashoatKeyserverID].currentUserInfo, + calendarResult, + messagesResult, + userInfos, + updatesCurrentAsOf, + logInActionSource: keyserverAuthInfo.logInActionSource, + notAcknowledgedPolicies: + responses[ashoatKeyserverID].notAcknowledgedPolicies, + }; + }; + +function useKeyserverAuth(): ( + input: KeyserverAuthInfo, +) => Promise { + return useKeyserverCall(keyserverAuth); +} + function mergeUserInfos( ...userInfoArrays: Array<$ReadOnlyArray> ): UserInfo[] { @@ -564,4 +682,6 @@ setAccessTokenActionType, deleteIdentityAccountActionTypes, useDeleteIdentityAccount, + keyserverAuthActionTypes, + useKeyserverAuth, }; diff --git a/lib/types/account-types.js b/lib/types/account-types.js --- a/lib/types/account-types.js +++ b/lib/types/account-types.js @@ -96,6 +96,8 @@ logInFromNativeSIWE: 'LOG_IN_FROM_NATIVE_SIWE', corruptedDatabaseDeletion: 'CORRUPTED_DATABASE_DELETION', refetchUserDataAfterAcknowledgment: 'REFETCH_USER_DATA_AFTER_ACKNOWLEDGMENT', + keyserverAuthFromNative: 'KEYSERVER_AUTH_FROM_NATIVE', + keyserverAuthFromWeb: 'KEYSERVER_AUTH_FROM_WEB', }); export type LogInActionSource = $Values; @@ -159,6 +161,44 @@ +notAcknowledgedPolicies?: $ReadOnlyArray, }; +export type KeyserverAuthResult = { + +threadInfos: RawThreadInfos, + +currentUserInfo?: ?LoggedInUserInfo, + +messagesResult: GenericMessagesResult, + +userInfos: $ReadOnlyArray, + +calendarResult: CalendarResult, + +updatesCurrentAsOf: { +[keyserverID: string]: number }, + +logInActionSource: LogInActionSource, + +notAcknowledgedPolicies?: ?$ReadOnlyArray, +}; + +type KeyserverRequestData = { + +initialContentEncryptedMessage: string, + +initialNotificationsEncryptedMessage: string, +}; + +export type KeyserverAuthInfo = { + +userID: string, + +deviceID: string, + +doNotRegister: boolean, + +calendarQuery: CalendarQuery, + +deviceTokenUpdateInput: DeviceTokenUpdateInput, + +logInActionSource: LogInActionSource, + +keyserverData: { +[keyserverID: string]: KeyserverRequestData }, +}; + +export type KeyserverAuthRequest = $ReadOnly<{ + ...KeyserverRequestData, + +userID: string, + +deviceID: string, + +doNotRegister: boolean, + +calendarQuery: CalendarQuery, + +deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest, + +watchedIDs: $ReadOnlyArray, + +platformDetails: PlatformDetails, + +source?: LogInActionSource, +}>; + export type UpdatePasswordRequest = { code: string, password: string, diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js --- a/lib/types/endpoints.js +++ b/lib/types/endpoints.js @@ -25,6 +25,7 @@ LOG_IN: 'log_in', UPDATE_PASSWORD: 'update_password', POLICY_ACKNOWLEDGMENT: 'policy_acknowledgment', + KEYSERVER_AUTH: 'keyserver_auth', }); type SessionChangingEndpoint = $Values;