diff --git a/web/grpc/identity-service-client-proxy.js b/web/grpc/identity-service-client-proxy.js deleted file mode 100644 index 6740f58cf..000000000 --- a/web/grpc/identity-service-client-proxy.js +++ /dev/null @@ -1,125 +0,0 @@ -// @flow - -import type { - OneTimeKeysResultValues, - SignedPrekeys, -} from 'lib/types/crypto-types.js'; -import type { - SignedDeviceList, - SignedMessage, - IdentityServiceClient, - IdentityServiceAuthLayer, - DeviceOlmOutboundKeys, - IdentityAuthResult, - UserDevicesOlmInboundKeys, - UserDevicesOlmOutboundKeys, -} from 'lib/types/identity-service-types.js'; - -import { - type CommSharedWorker, - getCommSharedWorker, -} from '../shared-worker/shared-worker-provider.js'; -import { getOpaqueWasmPath } from '../shared-worker/utils/constants.js'; -import { - workerRequestMessageTypes, - workerResponseMessageTypes, -} from '../types/worker-types.js'; - -class IdentityServiceClientSharedProxy implements IdentityServiceClient { - sharedWorkerPromise: Promise; - - constructor(authLayer: ?IdentityServiceAuthLayer) { - this.sharedWorkerPromise = (async () => { - const sharedWorker = await getCommSharedWorker(); - await sharedWorker.schedule({ - type: workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT, - opaqueWasmPath: getOpaqueWasmPath(), - authLayer, - }); - - return sharedWorker; - })(); - } - - proxyToWorker( - method: $Keys, - ): (...args: $ReadOnlyArray) => Promise { - return async (...args: $ReadOnlyArray) => { - const sharedWorker = await this.sharedWorkerPromise; - const result = await sharedWorker.schedule({ - type: workerRequestMessageTypes.CALL_IDENTITY_CLIENT_METHOD, - method, - args, - }); - - if (!result) { - throw new Error(`Worker identity call didn't return expected message`); - } else if ( - result.type !== workerResponseMessageTypes.CALL_IDENTITY_CLIENT_METHOD - ) { - throw new Error( - `Worker identity call didn't return expected message. Instead got: ${JSON.stringify( - result, - )}`, - ); - } - - // Worker should return a message with the corresponding return type - return (result.result: any); - }; - } - - deleteUser: () => Promise = this.proxyToWorker('deleteUser'); - - logOut: () => Promise = this.proxyToWorker('logOut'); - - getKeyserverKeys: (keyserverID: string) => Promise = - this.proxyToWorker('getKeyserverKeys'); - - getOutboundKeysForUser: ( - userID: string, - ) => Promise = this.proxyToWorker( - 'getOutboundKeysForUser', - ); - - getInboundKeysForUser: ( - userID: string, - ) => Promise = this.proxyToWorker( - 'getInboundKeysForUser', - ); - - uploadOneTimeKeys: (oneTimeKeys: OneTimeKeysResultValues) => Promise = - this.proxyToWorker('uploadOneTimeKeys'); - - logInPasswordUser: ( - username: string, - password: string, - ) => Promise = this.proxyToWorker('logInPasswordUser'); - - logInWalletUser: ( - walletAddress: string, - siweMessage: string, - siweSignature: string, - ) => Promise = this.proxyToWorker('logInWalletUser'); - - uploadKeysForRegisteredDeviceAndLogIn: ( - userID: string, - nonceChallengeResponse: SignedMessage, - ) => Promise = this.proxyToWorker( - 'uploadKeysForRegisteredDeviceAndLogIn', - ); - - generateNonce: () => Promise = this.proxyToWorker('generateNonce'); - - publishWebPrekeys: (prekeys: SignedPrekeys) => Promise = - this.proxyToWorker('publishWebPrekeys'); - - getDeviceListHistoryForUser: ( - userID: string, - sinceTimestamp?: number, - ) => Promise<$ReadOnlyArray> = this.proxyToWorker( - 'getDeviceListHistoryForUser', - ); -} - -export { IdentityServiceClientSharedProxy }; diff --git a/web/grpc/identity-service-context-provider.react.js b/web/grpc/identity-service-context-provider.react.js index e6d1edb4b..929fc92a2 100644 --- a/web/grpc/identity-service-context-provider.react.js +++ b/web/grpc/identity-service-context-provider.react.js @@ -1,86 +1,154 @@ // @flow +import _isEqual from 'lodash/fp/isEqual.js'; import * as React from 'react'; import { IdentityClientContext, type AuthMetadata, } from 'lib/shared/identity-client-context.js'; -import { getConfig } from 'lib/utils/config.js'; +import type { + IdentityServiceClient, + IdentityServiceAuthLayer, +} from 'lib/types/identity-service-types.js'; +import { getContentSigningKey } from 'lib/utils/crypto-utils.js'; -import { IdentityServiceClientSharedProxy } from './identity-service-client-proxy.js'; -import { IdentityServiceClientWrapper } from './identity-service-client-wrapper.js'; -import { - useGetNewDeviceKeyUpload, - useGetExistingDeviceKeyUpload, -} from '../account/account-hooks.js'; -import { usingSharedWorker } from '../crypto/olm-api.js'; import { useSelector } from '../redux/redux-utils.js'; +import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js'; +import { getOpaqueWasmPath } from '../shared-worker/utils/constants.js'; +import { + workerRequestMessageTypes, + workerResponseMessageTypes, +} from '../types/worker-types.js'; + +type CreateMethodWorkerProxy = ( + method: $Keys, +) => (...args: $ReadOnlyArray) => Promise; type Props = { +children: React.Node, }; function IdentityServiceContextProvider(props: Props): React.Node { const { children } = props; const userID = useSelector(state => state.currentUserInfo?.id); const accessToken = useSelector(state => state.commServicesAccessToken); - const deviceID = useSelector( - state => state.cryptoStore?.primaryIdentityKeys.ed25519, - ); - const getNewDeviceKeyUpload = useGetNewDeviceKeyUpload(); - const getExistingDeviceKeyUpload = useGetExistingDeviceKeyUpload(); - - const client = React.useMemo(() => { - let authLayer = null; - if (userID && deviceID && accessToken) { - authLayer = { - userID, - deviceID, - commServicesAccessToken: accessToken, - }; - } - if (usingSharedWorker) { - return new IdentityServiceClientSharedProxy(authLayer); - } else { - return new IdentityServiceClientWrapper( - getConfig().platformDetails, - null, - authLayer, - getNewDeviceKeyUpload, - getExistingDeviceKeyUpload, - ); - } - }, [ - accessToken, - deviceID, - getNewDeviceKeyUpload, - getExistingDeviceKeyUpload, - userID, - ]); - - const getAuthMetadata = React.useCallback<() => Promise>( - async () => ({ + + const getAuthMetadata = React.useCallback< + () => Promise, + >(async () => { + const contentSigningKey = await getContentSigningKey(); + return { userID, - deviceID, + deviceID: contentSigningKey, accessToken, - }), - [accessToken, deviceID, userID], + }; + }, [accessToken, userID]); + + const workerClientAuthMetadata = React.useRef(null); + const ensureThatWorkerClientAuthMetadataIsCurrent = + React.useCallback(async () => { + const [sharedWorker, authMetadata] = await Promise.all([ + getCommSharedWorker(), + getAuthMetadata(), + ]); + + if (_isEqual(authMetadata, workerClientAuthMetadata.current)) { + return; + } + + workerClientAuthMetadata.current = authMetadata; + + let authLayer: ?IdentityServiceAuthLayer = null; + if ( + authMetadata.userID && + authMetadata.deviceID && + authMetadata.accessToken + ) { + authLayer = { + userID: authMetadata.userID, + deviceID: authMetadata.deviceID, + commServicesAccessToken: authMetadata.accessToken, + }; + } + + await sharedWorker.schedule({ + type: workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT, + opaqueWasmPath: getOpaqueWasmPath(), + authLayer, + }); + }, [getAuthMetadata]); + + React.useEffect(() => { + void ensureThatWorkerClientAuthMetadataIsCurrent(); + }, [ensureThatWorkerClientAuthMetadataIsCurrent]); + + const proxyMethodToWorker: CreateMethodWorkerProxy = React.useCallback( + method => + async (...args: $ReadOnlyArray) => { + await ensureThatWorkerClientAuthMetadataIsCurrent(); + + const sharedWorker = await getCommSharedWorker(); + const result = await sharedWorker.schedule({ + type: workerRequestMessageTypes.CALL_IDENTITY_CLIENT_METHOD, + method, + args, + }); + + if (!result) { + throw new Error( + `Worker identity call didn't return expected message`, + ); + } else if ( + result.type !== workerResponseMessageTypes.CALL_IDENTITY_CLIENT_METHOD + ) { + throw new Error( + `Worker identity call didn't return expected message. Instead got: ${JSON.stringify( + result, + )}`, + ); + } + + // Worker should return a message with the corresponding return type + return (result.result: any); + }, + [ensureThatWorkerClientAuthMetadataIsCurrent], ); + const client = React.useMemo(() => { + return { + deleteUser: proxyMethodToWorker('deleteUser'), + logOut: proxyMethodToWorker('logOut'), + getKeyserverKeys: proxyMethodToWorker('getKeyserverKeys'), + getOutboundKeysForUser: proxyMethodToWorker('getOutboundKeysForUser'), + getInboundKeysForUser: proxyMethodToWorker('getInboundKeysForUser'), + uploadOneTimeKeys: proxyMethodToWorker('uploadOneTimeKeys'), + logInPasswordUser: proxyMethodToWorker('logInPasswordUser'), + logInWalletUser: proxyMethodToWorker('logInWalletUser'), + uploadKeysForRegisteredDeviceAndLogIn: proxyMethodToWorker( + 'uploadKeysForRegisteredDeviceAndLogIn', + ), + generateNonce: proxyMethodToWorker('generateNonce'), + publishWebPrekeys: proxyMethodToWorker('publishWebPrekeys'), + getDeviceListHistoryForUser: proxyMethodToWorker( + 'getDeviceListHistoryForUser', + ), + }; + }, [proxyMethodToWorker]); + const value = React.useMemo( () => ({ identityClient: client, getAuthMetadata, }), [client, getAuthMetadata], ); return ( {children} ); } export default IdentityServiceContextProvider;