Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit de4adf7

Browse files
committed
Report verification and recovery state to posthog
1 parent ed7a21a commit de4adf7

File tree

4 files changed

+384
-34
lines changed

4 files changed

+384
-34
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
},
6868
"dependencies": {
6969
"@babel/runtime": "^7.12.5",
70-
"@matrix-org/analytics-events": "^0.20.0",
70+
"@matrix-org/analytics-events": "^0.21.0",
7171
"@matrix-org/emojibase-bindings": "^1.1.2",
7272
"@matrix-org/matrix-wysiwyg": "2.17.0",
7373
"@matrix-org/olm": "3.2.15",

src/DeviceListener.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import {
2525
} from "matrix-js-sdk/src/matrix";
2626
import { logger } from "matrix-js-sdk/src/logger";
2727
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
28-
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
28+
import { CryptoApi, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
29+
import { CryptoSessionStateChange } from "@matrix-org/analytics-events/types/typescript/CryptoSessionStateChange";
2930

31+
import { PosthogAnalytics } from "./PosthogAnalytics";
3032
import dis from "./dispatcher/dispatcher";
3133
import {
3234
hideToast as hideBulkUnverifiedSessionsToast,
@@ -79,6 +81,10 @@ export default class DeviceListener {
7981
private enableBulkUnverifiedSessionsReminder = true;
8082
private deviceClientInformationSettingWatcherRef: string | undefined;
8183

84+
// Remember the current analytics state to avoid sending the same event multiple times.
85+
private analyticsVerificationState?: string;
86+
private analyticsRecoveryState?: string;
87+
8288
public static sharedInstance(): DeviceListener {
8389
if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
8490
return window.mxDeviceListener;
@@ -301,6 +307,7 @@ export default class DeviceListener {
301307
const crossSigningReady = await crypto.isCrossSigningReady();
302308
const secretStorageReady = await crypto.isSecretStorageReady();
303309
const allSystemsReady = crossSigningReady && secretStorageReady;
310+
await this.reportCryptoSessionStateToAnalytics(crypto, cli, secretStorageReady);
304311

305312
if (this.dismissedThisDeviceToast || allSystemsReady) {
306313
hideSetupEncryptionToast();
@@ -407,6 +414,72 @@ export default class DeviceListener {
407414
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
408415
}
409416

417+
/**
418+
* Reports current recovery state to analytics.
419+
* Checks if the session is verified and if the recovery is correctly setup (i.e all secrets known locally and in 4S).
420+
* @param crypto - the crypto module
421+
* @param cli - the matrix client
422+
* @param secretStorageReady - if the secret storage is ready
423+
* @private
424+
*/
425+
private async reportCryptoSessionStateToAnalytics(
426+
crypto: CryptoApi,
427+
cli: MatrixClient,
428+
secretStorageReady: boolean,
429+
): Promise<void> {
430+
//
431+
const crossSigningStatus = await crypto.getCrossSigningStatus();
432+
// const backupInfo = await crypto.getActiveSessionBackupVersion();
433+
const backupInfo = await this.getKeyBackupInfo();
434+
const is4SEnabled = (await cli.secretStorage.getDefaultKeyId()) != null;
435+
436+
const verificationState = crossSigningStatus.publicKeysOnDevice ? "Verified" : "NotVerified";
437+
438+
let recoveryState: "Disabled" | "Enabled" | "Incomplete";
439+
if (!is4SEnabled) {
440+
recoveryState = "Disabled";
441+
} else {
442+
const allCrossSigningSecretsCached =
443+
crossSigningStatus.privateKeysCachedLocally.masterKey &&
444+
crossSigningStatus.privateKeysCachedLocally.selfSigningKey &&
445+
crossSigningStatus.privateKeysCachedLocally.userSigningKey;
446+
if (backupInfo != null) {
447+
// there is a backup check that all secrets are stored in 4s and known locally
448+
// if they are not, the backup is incomplete
449+
const backupPrivateKeyIsInCache = (await crypto.getSessionBackupPrivateKey()) != null;
450+
if (secretStorageReady && allCrossSigningSecretsCached && backupPrivateKeyIsInCache) {
451+
recoveryState = "Enabled";
452+
} else {
453+
recoveryState = "Incomplete";
454+
}
455+
} else {
456+
// no backup just consider cross-signing secrets
457+
if (allCrossSigningSecretsCached && crossSigningStatus.privateKeysInSecretStorage) {
458+
recoveryState = "Enabled";
459+
} else {
460+
recoveryState = "Incomplete";
461+
}
462+
}
463+
}
464+
465+
if (this.analyticsVerificationState === verificationState && this.analyticsRecoveryState === recoveryState) {
466+
// No changes, no need to send the event nor update the user properties
467+
return;
468+
}
469+
this.analyticsRecoveryState = recoveryState;
470+
this.analyticsVerificationState = verificationState;
471+
472+
// Update user properties
473+
PosthogAnalytics.instance.setProperty("recoveryState", recoveryState);
474+
PosthogAnalytics.instance.setProperty("verificationState", verificationState);
475+
476+
PosthogAnalytics.instance.trackEvent<CryptoSessionStateChange>({
477+
eventName: "CryptoSessionState",
478+
verificationState: verificationState,
479+
recoveryState: recoveryState,
480+
});
481+
}
482+
410483
/**
411484
* Check if key backup is enabled, and if not, raise an `Action.ReportKeyBackupNotEnabled` event (which will
412485
* trigger an auto-rageshake).

0 commit comments

Comments
 (0)