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

Commit

Permalink
Report verification and recovery state to posthog (#12516)
Browse files Browse the repository at this point in the history
* Report verification and recovery state to posthog

* Fix CryptoApi import

* Fix js-sdk import

* Review: Use DeviceVerificationStatus instead of CrossSigningStatus

* Review: Clean condition to check secrets in 4S

* review: Fix redundent !!
  • Loading branch information
BillCarsonFr authored May 21, 2024
1 parent f712b80 commit a29cabe
Show file tree
Hide file tree
Showing 4 changed files with 450 additions and 5 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/analytics-events": "^0.20.0",
"@matrix-org/analytics-events": "^0.21.0",
"@matrix-org/emojibase-bindings": "^1.1.2",
"@matrix-org/matrix-wysiwyg": "2.17.0",
"@matrix-org/olm": "3.2.15",
Expand Down
71 changes: 71 additions & 0 deletions src/DeviceListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import {
import { logger } from "matrix-js-sdk/src/logger";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
import { CryptoSessionStateChange } from "@matrix-org/analytics-events/types/typescript/CryptoSessionStateChange";

import { PosthogAnalytics } from "./PosthogAnalytics";
import dis from "./dispatcher/dispatcher";
import {
hideToast as hideBulkUnverifiedSessionsToast,
Expand Down Expand Up @@ -79,6 +81,10 @@ export default class DeviceListener {
private enableBulkUnverifiedSessionsReminder = true;
private deviceClientInformationSettingWatcherRef: string | undefined;

// Remember the current analytics state to avoid sending the same event multiple times.
private analyticsVerificationState?: string;
private analyticsRecoveryState?: string;

public static sharedInstance(): DeviceListener {
if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
return window.mxDeviceListener;
Expand Down Expand Up @@ -301,6 +307,7 @@ export default class DeviceListener {
const crossSigningReady = await crypto.isCrossSigningReady();
const secretStorageReady = await crypto.isSecretStorageReady();
const allSystemsReady = crossSigningReady && secretStorageReady;
await this.reportCryptoSessionStateToAnalytics(cli);

if (this.dismissedThisDeviceToast || allSystemsReady) {
hideSetupEncryptionToast();
Expand Down Expand Up @@ -407,6 +414,70 @@ export default class DeviceListener {
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
}

/**
* Reports current recovery state to analytics.
* Checks if the session is verified and if the recovery is correctly set up (i.e all secrets known locally and in 4S).
* @param cli - the matrix client
* @private
*/
private async reportCryptoSessionStateToAnalytics(cli: MatrixClient): Promise<void> {
const crypto = cli.getCrypto()!;
const secretStorageReady = await crypto.isSecretStorageReady();
const crossSigningStatus = await crypto.getCrossSigningStatus();
const backupInfo = await this.getKeyBackupInfo();
const is4SEnabled = (await cli.secretStorage.getDefaultKeyId()) != null;
const deviceVerificationStatus = await crypto.getDeviceVerificationStatus(cli.getUserId()!, cli.getDeviceId()!);

const verificationState =
deviceVerificationStatus?.signedByOwner && deviceVerificationStatus?.crossSigningVerified
? "Verified"
: "NotVerified";

let recoveryState: "Disabled" | "Enabled" | "Incomplete";
if (!is4SEnabled) {
recoveryState = "Disabled";
} else {
const allCrossSigningSecretsCached =
crossSigningStatus.privateKeysCachedLocally.masterKey &&
crossSigningStatus.privateKeysCachedLocally.selfSigningKey &&
crossSigningStatus.privateKeysCachedLocally.userSigningKey;
if (backupInfo != null) {
// There is a backup. Check that all secrets are stored in 4S and known locally.
// If they are not, recovery is incomplete.
const backupPrivateKeyIsInCache = (await crypto.getSessionBackupPrivateKey()) != null;
if (secretStorageReady && allCrossSigningSecretsCached && backupPrivateKeyIsInCache) {
recoveryState = "Enabled";
} else {
recoveryState = "Incomplete";
}
} else {
// No backup. Just consider cross-signing secrets.
if (secretStorageReady && allCrossSigningSecretsCached) {
recoveryState = "Enabled";
} else {
recoveryState = "Incomplete";
}
}
}

if (this.analyticsVerificationState === verificationState && this.analyticsRecoveryState === recoveryState) {
// No changes, no need to send the event nor update the user properties
return;
}
this.analyticsRecoveryState = recoveryState;
this.analyticsVerificationState = verificationState;

// Update user properties
PosthogAnalytics.instance.setProperty("recoveryState", recoveryState);
PosthogAnalytics.instance.setProperty("verificationState", verificationState);

PosthogAnalytics.instance.trackEvent<CryptoSessionStateChange>({
eventName: "CryptoSessionState",
verificationState: verificationState,
recoveryState: recoveryState,
});
}

/**
* Check if key backup is enabled, and if not, raise an `Action.ReportKeyBackupNotEnabled` event (which will
* trigger an auto-rageshake).
Expand Down
Loading

0 comments on commit a29cabe

Please sign in to comment.