Skip to content

Commit 06b60c6

Browse files
committed
[PM-17121/17204] Fix fingerprint dialogs and disabled active biometric lock component (#12928)
* Fix biometrics unlock window being empty * Add trust on sensitive action * Add dialog for outdated desktop app and fix spelling * Use updated fingerprint method * Refactor connected app trust * Move connected apps to ephemeral value store and show error on outdated browser * Move trust logic to only occur when fingerprint setting is enabled * Add more tests * Simplify code * Update ephemeral value list call to "listEphemeralValueKeys" * Fix trust being ignored (cherry picked from commit ef20ca8)
1 parent b4e6496 commit 06b60c6

File tree

11 files changed

+508
-69
lines changed

11 files changed

+508
-69
lines changed

apps/browser/src/_locales/en/messages.json

+6
Original file line numberDiff line numberDiff line change
@@ -4903,5 +4903,11 @@
49034903
},
49044904
"extraWide": {
49054905
"message": "Extra wide"
4906+
},
4907+
"updateDesktopAppOrDisableFingerprintDialogTitle": {
4908+
"message": "Please update your desktop application"
4909+
},
4910+
"updateDesktopAppOrDisableFingerprintDialogMessage": {
4911+
"message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings."
49064912
}
49074913
}

apps/browser/src/background/nativeMessaging.background.ts

+20-18
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,6 @@ export class NativeMessagingBackground {
162162
HashAlgorithmForEncryption,
163163
);
164164

165-
if (this.validatingFingerprint) {
166-
this.validatingFingerprint = false;
167-
await this.biometricStateService.setFingerprintValidated(true);
168-
}
169165
this.sharedSecret = new SymmetricCryptoKey(decrypted);
170166
this.logService.info("[Native Messaging IPC] Secure channel established");
171167

@@ -200,15 +196,24 @@ export class NativeMessagingBackground {
200196
}
201197
return;
202198
case "verifyFingerprint": {
203-
if (this.sharedSecret == null) {
204-
this.logService.info(
205-
"[Native Messaging IPC] Desktop app requested trust verification by fingerprint.",
206-
);
207-
this.validatingFingerprint = true;
208-
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
209-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
210-
this.showFingerprintDialog();
211-
}
199+
this.logService.info("[Native Messaging IPC] Legacy app is requesting fingerprint");
200+
this.messagingService.send("showUpdateDesktopAppOrDisableFingerprintDialog", {});
201+
break;
202+
}
203+
case "verifyDesktopIPCFingerprint": {
204+
this.logService.info(
205+
"[Native Messaging IPC] Desktop app requested trust verification by fingerprint.",
206+
);
207+
await this.showFingerprintDialog();
208+
break;
209+
}
210+
case "verifiedDesktopIPCFingerprint": {
211+
await this.biometricStateService.setFingerprintValidated(true);
212+
this.messagingService.send("hideNativeMessagingFingerprintDialog", {});
213+
break;
214+
}
215+
case "rejectedDesktopIPCFingerprint": {
216+
this.messagingService.send("hideNativeMessagingFingerprintDialog", {});
212217
break;
213218
}
214219
case "wrongUserId":
@@ -426,12 +431,9 @@ export class NativeMessagingBackground {
426431
}
427432

428433
private async showFingerprintDialog() {
429-
const fingerprint = await this.keyService.getFingerprint(
430-
(await firstValueFrom(this.accountService.activeAccount$))?.id,
431-
this.publicKey,
432-
);
434+
const fingerprint = await this.keyService.getFingerprint(this.appId, this.publicKey);
433435

434-
this.messagingService.send("showNativeMessagingFinterprintDialog", {
436+
this.messagingService.send("showNativeMessagingFingerprintDialog", {
435437
fingerprint: fingerprint,
436438
});
437439
}

apps/browser/src/popup/app.component.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,18 @@ export class AppComponent implements OnInit, OnDestroy {
128128
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
129129
// eslint-disable-next-line @typescript-eslint/no-floating-promises
130130
this.showDialog(msg);
131-
} else if (msg.command === "showNativeMessagingFinterprintDialog") {
131+
} else if (msg.command === "showNativeMessagingFingerprintDialog") {
132132
// TODO: Should be refactored to live in another service.
133133
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
134134
// eslint-disable-next-line @typescript-eslint/no-floating-promises
135135
this.showNativeMessagingFingerprintDialog(msg);
136+
} else if (msg.command === "showUpdateDesktopAppOrDisableFingerprintDialog") {
137+
// TODO: Should be refactored to live in another service.
138+
await this.showDialog({
139+
title: this.i18nService.t("updateDesktopAppOrDisableFingerprintDialogTitle"),
140+
content: this.i18nService.t("updateDesktopAppOrDisableFingerprintDialogMessage"),
141+
type: "warning",
142+
});
136143
} else if (msg.command === "showToast") {
137144
this.toastService._showToast(msg);
138145
} else if (msg.command === "reloadProcess") {

apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts

+28-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { DIALOG_DATA } from "@angular/cdk/dialog";
2-
import { Component, Inject } from "@angular/core";
1+
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
2+
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
3+
import { filter, Subject, takeUntil } from "rxjs";
34

45
import { JslibModule } from "@bitwarden/angular/jslib.module";
6+
import { MessageListener } from "@bitwarden/common/platform/messaging";
57
import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components";
68

79
export type DesktopSyncVerificationDialogParams = {
@@ -13,8 +15,30 @@ export type DesktopSyncVerificationDialogParams = {
1315
standalone: true,
1416
imports: [JslibModule, ButtonModule, DialogModule],
1517
})
16-
export class DesktopSyncVerificationDialogComponent {
17-
constructor(@Inject(DIALOG_DATA) protected params: DesktopSyncVerificationDialogParams) {}
18+
export class DesktopSyncVerificationDialogComponent implements OnDestroy, OnInit {
19+
private destroy$ = new Subject<void>();
20+
21+
constructor(
22+
@Inject(DIALOG_DATA) protected params: DesktopSyncVerificationDialogParams,
23+
private dialogRef: DialogRef<DesktopSyncVerificationDialogComponent>,
24+
private messageListener: MessageListener,
25+
) {}
26+
27+
ngOnDestroy(): void {
28+
this.destroy$.next();
29+
this.destroy$.complete();
30+
}
31+
32+
ngOnInit(): void {
33+
this.messageListener.allMessages$
34+
.pipe(
35+
filter((m) => m.command === "hideNativeMessagingFingerprintDialog"),
36+
takeUntil(this.destroy$),
37+
)
38+
.subscribe(() => {
39+
this.dialogRef.close();
40+
});
41+
}
1842

1943
static open(dialogService: DialogService, data: DesktopSyncVerificationDialogParams) {
2044
return dialogService.open(DesktopSyncVerificationDialogComponent, {

apps/desktop/src/locales/en/messages.json

+15
Original file line numberDiff line numberDiff line change
@@ -3474,5 +3474,20 @@
34743474
},
34753475
"changeAcctEmail": {
34763476
"message": "Change account email"
3477+
},
3478+
"organizationUpgradeRequired": {
3479+
"message": "Organization upgrade required"
3480+
},
3481+
"upgradeOrganization": {
3482+
"message": "Upgrade organization"
3483+
},
3484+
"upgradeOrganizationDesc": {
3485+
"message": "This feature is not available for free organizations. Switch to a paid plan to unlock more features."
3486+
},
3487+
"updateBrowserOrDisableFingerprintDialogTitle": {
3488+
"message": "Extension update required"
3489+
},
3490+
"updateBrowserOrDisableFingerprintDialogMessage": {
3491+
"message": "The browser extension you are using is out of date. Please update it or disable browser integration fingerprint validation in the desktop app settings."
34773492
}
34783493
}

apps/desktop/src/platform/preload.ts

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ const ephemeralStore = {
123123
getEphemeralValue: (key: string): Promise<string> => ipcRenderer.invoke("getEphemeralValue", key),
124124
removeEphemeralValue: (key: string): Promise<void> =>
125125
ipcRenderer.invoke("deleteEphemeralValue", key),
126+
listEphemeralValueKeys: (): Promise<string[]> => ipcRenderer.invoke("listEphemeralValueKeys"),
126127
};
127128

128129
const localhostCallbackService = {

apps/desktop/src/platform/services/ephemeral-value-storage.main.service.ts

+3
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@ export class EphemeralValueStorageService {
1717
ipcMain.handle("deleteEphemeralValue", async (event, key: string) => {
1818
this.ephemeralValues.delete(key);
1919
});
20+
ipcMain.handle("listEphemeralValueKeys", async (event) => {
21+
return Array.from(this.ephemeralValues.keys());
22+
});
2023
}
2124
}

0 commit comments

Comments
 (0)