@wireapp/core 32.1.3 → 34.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,18 +3,18 @@
3
3
  "./src/main/cryptography/AssetCryptography/crypto.node": "./src/main/cryptography/AssetCryptography/crypto.browser.js"
4
4
  },
5
5
  "dependencies": {
6
- "@wireapp/api-client": "^20.6.11",
7
- "@wireapp/commons": "^4.4.8",
6
+ "@wireapp/api-client": "^21.0.0",
7
+ "@wireapp/commons": "^4.4.10",
8
8
  "@wireapp/core-crypto": "0.5.2",
9
9
  "@wireapp/cryptobox": "12.8.0",
10
- "@wireapp/promise-queue": "^1.3.2",
11
- "@wireapp/protocol-messaging": "1.38.0",
12
- "@wireapp/store-engine-dexie": "^1.7.8",
10
+ "@wireapp/promise-queue": "^1.3.4",
11
+ "@wireapp/protocol-messaging": "1.39.0",
12
+ "@wireapp/store-engine-dexie": "^1.7.10",
13
13
  "axios": "^0.27.2",
14
- "bazinga64": "5.11.8",
14
+ "bazinga64": "5.11.10",
15
15
  "hash.js": "1.1.7",
16
16
  "http-status-codes": "2.2.0",
17
- "idb": "7.0.2",
17
+ "idb": "7.1.0",
18
18
  "logdown": "3.3.1",
19
19
  "long": "4.0.0",
20
20
  "uuidjs": "4.2.12"
@@ -24,30 +24,22 @@
24
24
  "@babel/preset-env": "^7.19.1",
25
25
  "@babel/preset-typescript": "^7.18.6",
26
26
  "@faker-js/faker": "^7.6.0",
27
- "@open-wc/webpack-import-meta-loader": "0.4.7",
28
27
  "@types/babel__core": "^7",
29
28
  "@types/jest": "29.2.0",
30
29
  "@types/long": "4.0.1",
31
- "@types/node": "^14.18.29",
30
+ "@types/node": "^18.11.2",
32
31
  "@types/tough-cookie": "4.0.2",
33
32
  "@wireapp/commons": "workspace:^",
34
33
  "@wireapp/store-engine-dexie": "workspace:^",
35
- "commander": "8.0.0",
34
+ "commander": "9.4.1",
36
35
  "cross-env": "7.0.3",
37
36
  "dotenv-defaults": "5.0.2",
38
37
  "fake-indexeddb": "4.0.0",
39
- "istanbul": "1.1.0-alpha.1",
40
38
  "jest": "29.2.1",
41
- "jest-babel": "1.0.1",
42
39
  "jest-jasmine2": "29.2.1",
43
40
  "jest-websocket-mock": "2.4.0",
44
41
  "mock-socket": "9.1.5",
45
- "nock": "13.2.9",
46
- "nyc": "15.1.0",
47
- "rimraf": "3.0.2",
48
- "typescript": "4.8.4",
49
- "webpack": "4.46.0",
50
- "webpack-cli": "4.10.0"
42
+ "nock": "13.2.9"
51
43
  },
52
44
  "description": "Wire for Web's communication core.",
53
45
  "files": [
@@ -63,7 +55,6 @@
63
55
  "scripts": {
64
56
  "build": "tsc",
65
57
  "clean": "rimraf .tmp \"src/main/{!(*.test*).js,*.js.map,*.d.ts}\" \"src/main/test/{*.js.map,*.d.ts}\" \"src/main/!(test)/{!(*.test*).js,*.js.map,*.d.ts}\" \"src/main/**/{!(*.test*|AccountHelper|StoreHelper).js,*.js.map,*.d.ts}\" \"src/main/**/*.node.js\"",
66
- "coverage": "cross-env JASMINE_CONFIG_PATH=src/test/node/support/jasmine.json istanbul cover --report html node_modules/jasmine/bin/jasmine.js",
67
58
  "demo:composite": "cross-env NODE_DEBUG='@wireapp*' ts-node src/demo/composite.ts",
68
59
  "demo:sendCounter": "cross-env NODE_DEBUG='@wireapp*' ts-node src/demo/sendCounter.ts",
69
60
  "dist": "yarn clean && yarn build",
@@ -73,6 +64,6 @@
73
64
  "test": "jest",
74
65
  "watch": "tsc ---watch"
75
66
  },
76
- "version": "32.1.3",
77
- "gitHead": "2955478c0cc969992f01f92985c34b43265bbf3e"
67
+ "version": "34.0.0",
68
+ "gitHead": "28544331b51d4f6371ff71802271ecaa079da803"
78
69
  }
@@ -13,7 +13,7 @@ import { AssetService, ConversationService, PayloadBundleSource, PayloadBundleTy
13
13
  import * as OtrMessage from './conversation/message/OtrMessage';
14
14
  import * as UserMessage from './conversation/message/UserMessage';
15
15
  import { CoreError } from './CoreError';
16
- import { CryptographyService } from './cryptography/';
16
+ import { CryptographyService, SessionId } from './cryptography/';
17
17
  import { GiphyService } from './giphy/';
18
18
  import { HandledEventPayload, NotificationService } from './notification/';
19
19
  import { SelfService } from './self/';
@@ -84,6 +84,17 @@ interface AccountOptions<T> {
84
84
  */
85
85
  mlsConfig?: MLSConfig<T>;
86
86
  }
87
+ declare type InitOptions = {
88
+ /** cookie used to identify the current user. Will use the browser cookie if not defined */
89
+ cookie?: Cookie;
90
+ /** fully initiate the client and register periodic checks */
91
+ initClient?: boolean;
92
+ /**
93
+ * callback triggered when a message from an unknown client is received.
94
+ * An unknown client is a client we don't yet have a session with
95
+ */
96
+ onNewClient?: (sessionId: SessionId) => void;
97
+ };
87
98
  export declare class Account<T = any> extends EventEmitter {
88
99
  private readonly apiClient;
89
100
  private readonly logger;
@@ -130,9 +141,8 @@ export declare class Account<T = any> extends EventEmitter {
130
141
  * Will fail if local client cannot be found
131
142
  *
132
143
  * @param clientType The type of client the user is using (temporary or permanent)
133
- * @param cookie The cookie to identify the user against backend (will use the browser's one if not given)
134
144
  */
135
- init(clientType: ClientType, cookie?: Cookie, initClient?: boolean): Promise<Context>;
145
+ init(clientType: ClientType, { cookie, initClient, onNewClient }?: InitOptions): Promise<Context>;
136
146
  /**
137
147
  * Will log the user in with the given credential.
138
148
  * Will also create the local client and store it in DB
@@ -74,6 +74,7 @@ const team_1 = require("./team/");
74
74
  const user_1 = require("./user/");
75
75
  const account_1 = require("./account/");
76
76
  const linkPreview_1 = require("./linkPreview");
77
+ const core_crypto_1 = require("@wireapp/core-crypto");
77
78
  const ReconnectingWebsocket_1 = require("@wireapp/api-client/src/tcp/ReconnectingWebsocket");
78
79
  const encryptedStore_1 = require("./util/encryptedStore");
79
80
  const bazinga64_1 = require("bazinga64");
@@ -152,16 +153,29 @@ class Account extends events_1.EventEmitter {
152
153
  * Will fail if local client cannot be found
153
154
  *
154
155
  * @param clientType The type of client the user is using (temporary or permanent)
155
- * @param cookie The cookie to identify the user against backend (will use the browser's one if not given)
156
156
  */
157
- async init(clientType, cookie, initClient = true) {
157
+ async init(clientType, { cookie, initClient = true, onNewClient } = {}) {
158
158
  var _a, _b, _c;
159
159
  const context = await this.apiClient.init(clientType, cookie);
160
160
  await this.initServices(context);
161
+ /** @fixme
162
+ * When we will start migrating to CoreCrypto encryption/decryption, those hooks won't be available anymore
163
+ * We will need to implement
164
+ * - the mechanism to handle messages from an unknown sender
165
+ * - the mechanism to generate new prekeys when we reach a certain threshold of prekeys
166
+ */
167
+ this.service.cryptography.setCryptoboxHooks({
168
+ onNewPrekeys: async (prekeys) => {
169
+ this.logger.debug(`Received '${prekeys.length}' new PreKeys.`);
170
+ await this.apiClient.api.client.putClient(context.clientId, { prekeys });
171
+ this.logger.debug(`Successfully uploaded '${prekeys.length}' PreKeys.`);
172
+ },
173
+ onNewSession: onNewClient,
174
+ });
161
175
  // Assumption: client gets only initialized once
162
176
  if (initClient) {
163
177
  await this.initClient({ clientType });
164
- if (this.mlsConfig) {
178
+ if (this.mlsConfig && this.backendFeatures.supportsMLS) {
165
179
  // initialize schedulers for pending mls proposals once client is initialized
166
180
  await ((_a = this.service) === null || _a === void 0 ? void 0 : _a.notification.checkExistingPendingProposals());
167
181
  // initialize schedulers for renewing key materials
@@ -300,7 +314,7 @@ class Account extends events_1.EventEmitter {
300
314
  const loadedClient = await this.service.client.getLocalClient();
301
315
  await this.apiClient.api.client.getClient(loadedClient.id);
302
316
  this.apiClient.context.clientId = loadedClient.id;
303
- if (this.mlsConfig) {
317
+ if (this.mlsConfig && this.backendFeatures.supportsMLS) {
304
318
  this.coreCryptoClient = await this.createMLSClient(loadedClient, this.apiClient.context, this.mlsConfig, entropyData);
305
319
  }
306
320
  return loadedClient;
@@ -310,10 +324,9 @@ class Account extends events_1.EventEmitter {
310
324
  throw new Error('Services are not set.');
311
325
  }
312
326
  const coreCryptoKeyId = 'corecrypto-key';
313
- const { CoreCrypto } = await Promise.resolve().then(() => __importStar(require('@wireapp/core-crypto')));
314
327
  const dbName = this.generateSecretsDbName(context);
315
- const secretStore = mlsConfig.secretsCrypto
316
- ? await (0, encryptedStore_1.createCustomEncryptedStore)(dbName, mlsConfig.secretsCrypto)
328
+ const secretStore = mlsConfig.systemCrypto
329
+ ? await (0, encryptedStore_1.createCustomEncryptedStore)(dbName, mlsConfig.systemCrypto)
317
330
  : await (0, encryptedStore_1.createEncryptedStore)(dbName);
318
331
  let key = await secretStore.getsecretValue(coreCryptoKeyId);
319
332
  let isNewMLSDevice = false;
@@ -324,7 +337,7 @@ class Account extends events_1.EventEmitter {
324
337
  isNewMLSDevice = true;
325
338
  }
326
339
  const { userId, domain } = this.apiClient.context;
327
- const mlsClient = await CoreCrypto.init({
340
+ const mlsClient = await core_crypto_1.CoreCrypto.init({
328
341
  databaseName: `corecrypto-${this.generateDbName(context)}`,
329
342
  key: bazinga64_1.Encoder.toBase64(key).asString,
330
343
  clientId: `${userId}:${client.id}@${domain}`,
@@ -344,7 +357,7 @@ class Account extends events_1.EventEmitter {
344
357
  }
345
358
  this.logger.info(`Creating new client {mls: ${!!this.mlsConfig}}`);
346
359
  const registeredClient = await this.service.client.register(loginData, clientInfo, entropyData);
347
- if (this.mlsConfig) {
360
+ if (this.mlsConfig && this.backendFeatures.supportsMLS) {
348
361
  this.coreCryptoClient = await this.createMLSClient(registeredClient, this.apiClient.context, this.mlsConfig, entropyData);
349
362
  }
350
363
  this.apiClient.context.clientId = registeredClient.id;
@@ -12,6 +12,11 @@ export declare type DecryptionError = {
12
12
  code: number;
13
13
  message: string;
14
14
  };
15
+ export declare type SessionId = {
16
+ userId: string;
17
+ clientId: string;
18
+ domain?: string;
19
+ };
15
20
  export interface MetaClient extends RegisteredClient {
16
21
  meta: {
17
22
  is_verified?: boolean;
@@ -30,7 +35,19 @@ export declare class CryptographyService {
30
35
  nbPrekeys: number;
31
36
  });
32
37
  constructSessionId(userId: string | QualifiedId, clientId: string, domain?: string): string;
38
+ private isSessionId;
39
+ /**
40
+ * Splits a sessionId into userId, clientId & domain (if any).
41
+ */
42
+ parseSessionId(sessionId: string): SessionId;
33
43
  static convertArrayRecipientsToBase64(recipients: OTRRecipients<Uint8Array>): OTRRecipients<string>;
44
+ setCryptoboxHooks({ onNewPrekeys, onNewSession, }: {
45
+ onNewPrekeys?: (prekeys: {
46
+ id: number;
47
+ key: string;
48
+ }[]) => void;
49
+ onNewSession?: (sessionId: SessionId) => void;
50
+ }): void;
34
51
  static convertBase64RecipientsToArray(recipients: OTRRecipients<string>): OTRRecipients<Uint8Array>;
35
52
  createCryptobox(entropyData?: Uint8Array): Promise<SerializedPreKey[]>;
36
53
  decrypt(sessionId: string, encodedCiphertext: string): Promise<Uint8Array>;
@@ -48,6 +48,21 @@ class CryptographyService {
48
48
  const baseId = `${id}@${clientId}`;
49
49
  return baseDomain && this.config.useQualifiedIds ? `${baseDomain}@${baseId}` : baseId;
50
50
  }
51
+ isSessionId(object) {
52
+ return object.userId && object.clientId;
53
+ }
54
+ /**
55
+ * Splits a sessionId into userId, clientId & domain (if any).
56
+ */
57
+ parseSessionId(sessionId) {
58
+ // see https://regex101.com/r/c8FtCw/1
59
+ const regex = /((?<domain>.+)@)?(?<userId>.+)@(?<clientId>.+)$/g;
60
+ const match = regex.exec(sessionId);
61
+ if (!match || !this.isSessionId(match.groups)) {
62
+ throw new Error(`given session id "${sessionId}" has wrong format`);
63
+ }
64
+ return match.groups;
65
+ }
51
66
  static convertArrayRecipientsToBase64(recipients) {
52
67
  return Object.fromEntries(Object.entries(recipients).map(([userId, otrClientMap]) => {
53
68
  const otrClientMapWithBase64 = Object.fromEntries(Object.entries(otrClientMap).map(([clientId, payload]) => {
@@ -56,6 +71,17 @@ class CryptographyService {
56
71
  return [userId, otrClientMapWithBase64];
57
72
  }));
58
73
  }
74
+ setCryptoboxHooks({ onNewPrekeys, onNewSession, }) {
75
+ if (onNewPrekeys) {
76
+ this.cryptobox.on(cryptobox_1.Cryptobox.TOPIC.NEW_PREKEYS, prekeys => {
77
+ const serializedPreKeys = prekeys.map(this.cryptobox.serialize_prekey);
78
+ onNewPrekeys(serializedPreKeys);
79
+ });
80
+ }
81
+ if (onNewSession) {
82
+ this.cryptobox.on(cryptobox_1.Cryptobox.TOPIC.NEW_SESSION, sessionId => onNewSession(this.parseSessionId(sessionId)));
83
+ }
84
+ }
59
85
  static convertBase64RecipientsToArray(recipients) {
60
86
  return Object.fromEntries(Object.entries(recipients).map(([userId, otrClientMap]) => {
61
87
  const otrClientMapWithUint8Array = Object.fromEntries(Object.entries(otrClientMap).map(([clientId, payload]) => {
@@ -9,7 +9,7 @@ export declare class MLSService {
9
9
  private readonly coreCryptoClientProvider;
10
10
  groupIdFromConversationId?: MLSCallbacks['groupIdFromConversationId'];
11
11
  constructor(config: MLSConfig | undefined, apiClient: APIClient, coreCryptoClientProvider: () => CoreCrypto | undefined);
12
- private getCoreCryptoClient;
12
+ private get coreCryptoClient();
13
13
  private uploadCommitBundle;
14
14
  addUsersToExistingConversation(groupId: Uint8Array, invitee: Invitee[]): Promise<import("@wireapp/api-client/src/conversation").PostMlsMessageResponse | null>;
15
15
  configureMLSCallbacks({ groupIdFromConversationId, ...coreCryptoCallbacks }: MLSCallbacks): void;
@@ -45,7 +45,7 @@ class MLSService {
45
45
  this.apiClient = apiClient;
46
46
  this.coreCryptoClientProvider = coreCryptoClientProvider;
47
47
  }
48
- getCoreCryptoClient() {
48
+ get coreCryptoClient() {
49
49
  const client = this.coreCryptoClientProvider();
50
50
  if (!client) {
51
51
  throw new Error('Could not get coreCryptoClient');
@@ -53,13 +53,12 @@ class MLSService {
53
53
  return client;
54
54
  }
55
55
  async uploadCommitBundle(groupId, { commit, welcome }) {
56
- const coreCryptoClient = this.getCoreCryptoClient();
57
56
  if (commit) {
58
57
  try {
59
58
  const messageResponse = await this.apiClient.api.conversation.postMlsMessage(
60
59
  //@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
61
60
  (0, exports.optionalToUint8Array)(commit));
62
- await coreCryptoClient.commitAccepted(groupId);
61
+ await this.coreCryptoClient.commitAccepted(groupId);
63
62
  if (welcome) {
64
63
  // If the commit went well, we can send the Welcome
65
64
  //@todo: it's temporary - we wait for core-crypto fix to return the actual Uint8Array instead of regular array
@@ -68,17 +67,17 @@ class MLSService {
68
67
  return messageResponse;
69
68
  }
70
69
  catch (error) {
71
- await coreCryptoClient.clearPendingCommit(groupId);
70
+ await this.coreCryptoClient.clearPendingCommit(groupId);
72
71
  }
73
72
  }
74
73
  return null;
75
74
  }
76
75
  addUsersToExistingConversation(groupId, invitee) {
77
- return this.processCommitAction(groupId, () => this.getCoreCryptoClient().addClientsToConversation(groupId, invitee));
76
+ return this.processCommitAction(groupId, () => this.coreCryptoClient.addClientsToConversation(groupId, invitee));
78
77
  }
79
78
  configureMLSCallbacks(_a) {
80
79
  var { groupIdFromConversationId } = _a, coreCryptoCallbacks = __rest(_a, ["groupIdFromConversationId"]);
81
- this.getCoreCryptoClient().registerCallbacks(Object.assign(Object.assign({}, coreCryptoCallbacks), { clientIdBelongsToOneOf: (client, otherClients) => {
80
+ this.coreCryptoClient.registerCallbacks(Object.assign(Object.assign({}, coreCryptoCallbacks), { clientIdBelongsToOneOf: (client, otherClients) => {
82
81
  const decoder = new TextDecoder();
83
82
  const { user } = (0, fullyQualifiedClientIdUtils_1.parseFullQualifiedClientId)(decoder.decode(client));
84
83
  return otherClients.some(client => {
@@ -113,22 +112,22 @@ class MLSService {
113
112
  return coreCryptoKeyPackagesPayload;
114
113
  }
115
114
  getEpoch(groupId) {
116
- return this.getCoreCryptoClient().conversationEpoch(groupId);
115
+ return this.coreCryptoClient.conversationEpoch(groupId);
117
116
  }
118
117
  async newProposal(proposalType, args) {
119
- return this.getCoreCryptoClient().newProposal(proposalType, args);
118
+ return this.coreCryptoClient.newProposal(proposalType, args);
120
119
  }
121
120
  async newExternalProposal(externalProposalType, args) {
122
- return this.getCoreCryptoClient().newExternalProposal(externalProposalType, args);
121
+ return this.coreCryptoClient.newExternalProposal(externalProposalType, args);
123
122
  }
124
123
  async processWelcomeMessage(welcomeMessage) {
125
- return this.getCoreCryptoClient().processWelcomeMessage(welcomeMessage);
124
+ return this.coreCryptoClient.processWelcomeMessage(welcomeMessage);
126
125
  }
127
126
  async decryptMessage(conversationId, payload) {
128
- return this.getCoreCryptoClient().decryptMessage(conversationId, payload);
127
+ return this.coreCryptoClient.decryptMessage(conversationId, payload);
129
128
  }
130
129
  async encryptMessage(conversationId, message) {
131
- return this.getCoreCryptoClient().encryptMessage(conversationId, message);
130
+ return this.coreCryptoClient.encryptMessage(conversationId, message);
132
131
  }
133
132
  /**
134
133
  * Will wrap a coreCrypto call that generates a CommitBundle and do all the necessary work so that commitbundle is handled the right way.
@@ -148,26 +147,26 @@ class MLSService {
148
147
  });
149
148
  }
150
149
  updateKeyingMaterial(conversationId) {
151
- return this.processCommitAction(conversationId, () => this.getCoreCryptoClient().updateKeyingMaterial(conversationId));
150
+ return this.processCommitAction(conversationId, () => this.coreCryptoClient.updateKeyingMaterial(conversationId));
152
151
  }
153
152
  async createConversation(conversationId, configuration) {
154
- return this.getCoreCryptoClient().createConversation(conversationId, configuration);
153
+ return this.coreCryptoClient.createConversation(conversationId, configuration);
155
154
  }
156
155
  removeClientsFromConversation(conversationId, clientIds) {
157
- return this.processCommitAction(conversationId, () => this.getCoreCryptoClient().removeClientsFromConversation(conversationId, clientIds));
156
+ return this.processCommitAction(conversationId, () => this.coreCryptoClient.removeClientsFromConversation(conversationId, clientIds));
158
157
  }
159
158
  async commitPendingProposals(groupId) {
160
- const commitBundle = await this.getCoreCryptoClient().commitPendingProposals(groupId);
159
+ const commitBundle = await this.coreCryptoClient.commitPendingProposals(groupId);
161
160
  return commitBundle ? void (await this.uploadCommitBundle(groupId, commitBundle)) : undefined;
162
161
  }
163
162
  async conversationExists(conversationId) {
164
- return this.getCoreCryptoClient().conversationExists(conversationId);
163
+ return this.coreCryptoClient.conversationExists(conversationId);
165
164
  }
166
165
  async clientValidKeypackagesCount() {
167
- return this.getCoreCryptoClient().clientValidKeypackagesCount();
166
+ return this.coreCryptoClient.clientValidKeypackagesCount();
168
167
  }
169
168
  async clientKeypackages(amountRequested) {
170
- return this.getCoreCryptoClient().clientKeypackages(amountRequested);
169
+ return this.coreCryptoClient.clientKeypackages(amountRequested);
171
170
  }
172
171
  /**
173
172
  * Will make the given client mls capable (generate and upload key packages)
@@ -184,7 +183,7 @@ class MLSService {
184
183
  return this.apiClient.api.client.uploadMLSKeyPackages(clientId, keypackages.map(keypackage => btoa(bazinga64_1.Converter.arrayBufferViewToBaselineString(keypackage))));
185
184
  }
186
185
  async wipeConversation(conversationId) {
187
- return this.getCoreCryptoClient().wipeConversation(conversationId);
186
+ return this.coreCryptoClient.wipeConversation(conversationId);
188
187
  }
189
188
  }
190
189
  exports.MLSService = MLSService;
@@ -18,7 +18,7 @@ export interface MLSConfig<T = any> {
18
18
  * encrypt/decrypt function pair that will be called before storing/fetching secrets in the secrets database.
19
19
  * If not provided will use the built in encryption mechanism
20
20
  */
21
- secretsCrypto?: SecretCrypto<T>;
21
+ systemCrypto?: SecretCrypto<T>;
22
22
  /**
23
23
  * path on the public server to the core crypto wasm file.
24
24
  * This file will be downloaded lazily when corecrypto is needed.
@@ -71,6 +71,7 @@ const NotificationDatabaseRepository_1 = require("./NotificationDatabaseReposito
71
71
  const protocol_messaging_1 = require("@wireapp/protocol-messaging");
72
72
  const bazinga64_1 = require("bazinga64");
73
73
  const TaskScheduler_1 = require("../util/TaskScheduler/TaskScheduler");
74
+ const mls_1 = require("../mls");
74
75
  const LowPrecisionTaskScheduler_1 = require("../util/LowPrecisionTaskScheduler/LowPrecisionTaskScheduler");
75
76
  const keyPackagesStatusStore_1 = require("../mls/keyPackagesStatusStore/keyPackagesStatusStore");
76
77
  const keyMaterialUpdatesStore_1 = require("../mls/keyMaterialUpdatesStore");
@@ -260,7 +261,12 @@ class NotificationService extends events_1.EventEmitter {
260
261
  const encryptedData = bazinga64_1.Decoder.fromBase64(event.data).asBytes;
261
262
  const groupId = await this.getGroupIdFromConversationId((_a = event.qualified_conversation) !== null && _a !== void 0 ? _a : { id: event.conversation, domain: '' });
262
263
  const groupIdBytes = bazinga64_1.Decoder.fromBase64(groupId).asBytes;
263
- const { proposals, commitDelay, message } = await this.mlsService.decryptMessage(groupIdBytes, encryptedData);
264
+ const { proposals, commitDelay, message, senderClientId: encodedSenderClientId, } = await this.mlsService.decryptMessage(groupIdBytes, encryptedData);
265
+ if (encodedSenderClientId) {
266
+ const decoder = new TextDecoder();
267
+ const senderClientId = decoder.decode((0, mls_1.optionalToUint8Array)(encodedSenderClientId));
268
+ event.senderClientId = senderClientId;
269
+ }
264
270
  // Check if the message includes proposals
265
271
  if (typeof commitDelay === 'number' || proposals.length > 0) {
266
272
  // we are dealing with a proposal, add a task to process this proposal later on
@@ -283,7 +289,10 @@ class NotificationService extends events_1.EventEmitter {
283
289
  * @todo Find a proper solution to add mappedEvent to this return
284
290
  * otherwise event.data will be base64 raw data of the received event
285
291
  */
286
- return { event, decryptedData };
292
+ return {
293
+ event,
294
+ decryptedData,
295
+ };
287
296
  // Encrypted Proteus events
288
297
  case Events.CONVERSATION_EVENT.OTR_MESSAGE_ADD: {
289
298
  if (dryRun) {