diff --git a/web/crypto/olm-api.js b/web/crypto/olm-api.js --- a/web/crypto/olm-api.js +++ b/web/crypto/olm-api.js @@ -152,4 +152,4 @@ }, }; -export { olmAPI }; +export { olmAPI, usingSharedWorker }; diff --git a/web/grpc/identity-service-client-proxy.js b/web/grpc/identity-service-client-proxy.js new file mode 100644 --- /dev/null +++ b/web/grpc/identity-service-client-proxy.js @@ -0,0 +1,109 @@ +// @flow + +import type { + OneTimeKeysResultValues, + SignedPrekeys, +} from 'lib/types/crypto-types.js'; +import type { + IdentityServiceClient, + IdentityServiceAuthLayer, + DeviceOlmOutboundKeys, + IdentityAuthResult, + UserDevicesOlmInboundKeys, + UserDevicesOlmOutboundKeys, +} from 'lib/types/identity-service-types.js'; +import { getConfig } from 'lib/utils/config.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(), + platformDetails: getConfig().platformDetails, + 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'); + + 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'); + + generateNonce: () => Promise = this.proxyToWorker('generateNonce'); + + publishWebPrekeys: (prekeys: SignedPrekeys) => Promise = + this.proxyToWorker('publishWebPrekeys'); +} + +export { IdentityServiceClientSharedProxy }; diff --git a/web/grpc/identity-service-context-provider.react.js b/web/grpc/identity-service-context-provider.react.js --- a/web/grpc/identity-service-context-provider.react.js +++ b/web/grpc/identity-service-context-provider.react.js @@ -8,8 +8,10 @@ } from 'lib/shared/identity-client-context.js'; import { getConfig } from 'lib/utils/config.js'; +import { IdentityServiceClientSharedProxy } from './identity-service-client-proxy.js'; import { IdentityServiceClientWrapper } from './identity-service-client-wrapper.js'; import { useGetDeviceKeyUpload } from '../account/account-hooks.js'; +import { usingSharedWorker } from '../crypto/olm-api.js'; import { useSelector } from '../redux/redux-utils.js'; type Props = { @@ -34,12 +36,16 @@ commServicesAccessToken: accessToken, }; } - return new IdentityServiceClientWrapper( - getConfig().platformDetails, - null, - authLayer, - getDeviceKeyUpload, - ); + if (usingSharedWorker) { + return new IdentityServiceClientSharedProxy(authLayer); + } else { + return new IdentityServiceClientWrapper( + getConfig().platformDetails, + null, + authLayer, + getDeviceKeyUpload, + ); + } }, [accessToken, deviceID, getDeviceKeyUpload, userID]); const getAuthMetadata = React.useCallback<() => Promise>( diff --git a/web/shared-worker/shared-worker-provider.js b/web/shared-worker/shared-worker-provider.js --- a/web/shared-worker/shared-worker-provider.js +++ b/web/shared-worker/shared-worker-provider.js @@ -162,4 +162,4 @@ return newModule; } -export { getCommSharedWorker }; +export { CommSharedWorker, getCommSharedWorker }; diff --git a/web/shared-worker/worker/identity-client.js b/web/shared-worker/worker/identity-client.js --- a/web/shared-worker/worker/identity-client.js +++ b/web/shared-worker/worker/identity-client.js @@ -2,10 +2,11 @@ import { getDeviceKeyUpload } from './worker-crypto.js'; import { IdentityServiceClientWrapper } from '../../grpc/identity-service-client-wrapper.js'; -import { workerRequestMessageTypes } from '../../types/worker-types.js'; -import type { - WorkerResponseMessage, - WorkerRequestMessage, +import { + type WorkerResponseMessage, + type WorkerRequestMessage, + workerRequestMessageTypes, + workerResponseMessageTypes, } from '../../types/worker-types.js'; import type { EmscriptenModule } from '../types/module'; import type { SQLiteQueryExecutor } from '../types/sqlite-query-executor'; @@ -26,7 +27,31 @@ message.authLayer, async () => getDeviceKeyUpload(), ); + return undefined; } + + if (!identityClient) { + throw new Error('Identity client not created'); + } + + if (message.type === workerRequestMessageTypes.CALL_IDENTITY_CLIENT_METHOD) { + // Flow doesn't allow us to access methods like this (it needs an index + // signature declaration in the object type) + // $FlowFixMe + const method = identityClient[message.method]; + if (typeof method !== 'function') { + throw new Error( + `Couldn't find identity client method with name '${message.method}'`, + ); + } + const result = await method(...message.args); + return { + type: workerResponseMessageTypes.CALL_IDENTITY_CLIENT_METHOD, + result, + }; + } + + return undefined; } function getIdentityClient(): ?IdentityServiceClientWrapper { diff --git a/web/types/worker-types.js b/web/types/worker-types.js --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -3,7 +3,10 @@ import type { AuthMetadata } from 'lib/shared/identity-client-context.js'; import type { CryptoStore } from 'lib/types/crypto-types.js'; import type { PlatformDetails } from 'lib/types/device-types.js'; -import type { IdentityServiceAuthLayer } from 'lib/types/identity-service-types.js'; +import type { + IdentityServiceClient, + IdentityServiceAuthLayer, +} from 'lib/types/identity-service-types.js'; import type { ClientDBStore, ClientDBStoreOperations, @@ -25,6 +28,7 @@ BACKUP_RESTORE: 11, INITIALIZE_CRYPTO_ACCOUNT: 12, CREATE_IDENTITY_SERVICE_CLIENT: 13, + CALL_IDENTITY_CLIENT_METHOD: 14, }); export const workerWriteRequests: $ReadOnlyArray = [ @@ -42,6 +46,7 @@ export const workerIdentityClientRequests: $ReadOnlyArray = [ workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT, + workerRequestMessageTypes.CALL_IDENTITY_CLIENT_METHOD, ]; export type PingWorkerRequestMessage = { @@ -121,6 +126,12 @@ +authLayer: ?IdentityServiceAuthLayer, }; +export type CallIdentityClientMethodRequestMessage = { + +type: 14, + +method: $Keys, + +args: $ReadOnlyArray, +}; + export type WorkerRequestMessage = | PingWorkerRequestMessage | InitWorkerRequestMessage @@ -135,7 +146,8 @@ | ClearSensitiveDataRequestMessage | BackupRestoreRequestMessage | InitializeCryptoAccountRequestMessage - | CreateIdentityServiceClientRequestMessage; + | CreateIdentityServiceClientRequestMessage + | CallIdentityClientMethodRequestMessage; export type WorkerRequestProxyMessage = { +id: number, @@ -148,6 +160,7 @@ CLIENT_STORE: 1, GET_CURRENT_USER_ID: 2, GET_PERSIST_STORAGE_ITEM: 3, + CALL_IDENTITY_CLIENT_METHOD: 4, }); export type PongWorkerResponseMessage = { @@ -170,11 +183,17 @@ +item: string, }; +export type CallIdentityClientMethodResponseMessage = { + +type: 4, + +result: mixed, +}; + export type WorkerResponseMessage = | PongWorkerResponseMessage | ClientStoreResponseMessage | GetCurrentUserIDResponseMessage - | GetPersistStorageItemResponseMessage; + | GetPersistStorageItemResponseMessage + | CallIdentityClientMethodResponseMessage; export type WorkerResponseProxyMessage = { +id?: number,