From 7819dbdd56fcd72aa02cf78dca5caec3b35fefe1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Fri, 17 May 2024 08:54:19 -0400 Subject: [PATCH] PM-8197 Do not allow browser biometric for locked account (#9216) Process reload is the means by which we protect user keys in memory. once an account locks, it triggers a process reload (assuming no other accounts are unlocked), that frees renderer memory. However, if the user is not unlocked, it is not protected by the process reload, so we may keep user keys in memory. --- apps/browser/src/_locales/en/messages.json | 6 ++++++ .../background/nativeMessaging.background.ts | 9 +++++++++ .../src/services/native-messaging.service.ts | 18 +++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 1854f1f6ff..28ad7b2d42 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1714,6 +1714,12 @@ "biometricsNotSupportedDesc": { "message": "Browser biometrics is not supported on this device." }, + "biometricsNotUnlockedTitle": { + "message": "User locked or logged out" + }, + "biometricsNotUnlockedDesc": { + "message": "Please unlock this user in the desktop application and try again." + }, "biometricsFailedTitle": { "message": "Biometrics failed" }, diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 51ab301fd1..534a239a81 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -321,6 +321,15 @@ export class NativeMessagingBackground { type: "danger", }); break; + } else if (message.response === "not unlocked") { + this.messagingService.send("showDialog", { + title: { key: "biometricsNotUnlockedTitle" }, + content: { key: "biometricsNotUnlockedDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + break; } else if (message.response === "canceled") { break; } diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 48bdc60047..06881b7463 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -1,8 +1,10 @@ import { Injectable, NgZone } from "@angular/core"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -43,6 +45,7 @@ export class NativeMessagingService { private nativeMessageHandler: NativeMessageHandlerService, private dialogService: DialogService, private accountService: AccountService, + private authService: AuthService, private ngZone: NgZone, ) {} @@ -137,6 +140,19 @@ export class NativeMessagingService { return this.send({ command: "biometricUnlock", response: "not supported" }, appId); } + const userId = + (message.userId as UserId) ?? + (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); + + if (userId == null) { + return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId); + } + + const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); + if (authStatus !== AuthenticationStatus.Unlocked) { + return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId); + } + const biometricUnlockPromise = message.userId == null ? firstValueFrom(this.biometricStateService.biometricUnlockEnabled$)