1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-21 03:43:41 +00:00

[PM-6296] Fix biometrics error prompt when biometrics are temporarily unavailable in browser extension (v2) (#10374)

* Create unavailable message for biometrics when in clamshell mode

* Move browser biometrics

* Inject nativemessagingbackground instead of using constructor

* Fix linting

* Fix build on browser
This commit is contained in:
Bernd Schoolmann
2024-08-27 08:25:20 +02:00
committed by GitHub
parent 9459cda304
commit 3c9b3ea2cc
38 changed files with 326 additions and 178 deletions

View File

@@ -3,7 +3,7 @@ import { systemPreferences } from "electron";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { passwords } from "@bitwarden/desktop-napi";
import { OsBiometricService } from "./biometrics.service.abstraction";
import { OsBiometricService } from "./desktop.biometrics.service";
export default class BiometricDarwinMain implements OsBiometricService {
constructor(private i18nservice: I18nService) {}

View File

@@ -1,4 +1,4 @@
import { OsBiometricService } from "./biometrics.service.abstraction";
import { OsBiometricService } from "./desktop.biometrics.service";
export default class NoopBiometricsService implements OsBiometricService {
constructor() {}

View File

@@ -7,7 +7,7 @@ import { biometrics, passwords } from "@bitwarden/desktop-napi";
import { WindowMain } from "../../../main/window.main";
import { isFlatpak, isLinux, isSnapStore } from "../../../utils";
import { OsBiometricService } from "./biometrics.service.abstraction";
import { OsBiometricService } from "./desktop.biometrics.service";
const polkitPolicy = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC

View File

@@ -6,7 +6,7 @@ import { biometrics, passwords } from "@bitwarden/desktop-napi";
import { WindowMain } from "../../../main/window.main";
import { OsBiometricService } from "./biometrics.service.abstraction";
import { OsBiometricService } from "./desktop.biometrics.service";
const KEY_WITNESS_SUFFIX = "_witness";
const WITNESS_VALUE = "known key";

View File

@@ -11,7 +11,7 @@ import { WindowMain } from "../../../main/window.main";
import BiometricDarwinMain from "./biometric.darwin.main";
import BiometricWindowsMain from "./biometric.windows.main";
import { BiometricsService } from "./biometrics.service";
import { OsBiometricService } from "./biometrics.service.abstraction";
import { OsBiometricService } from "./desktop.biometrics.service";
jest.mock("@bitwarden/desktop-napi", () => {
return {

View File

@@ -6,9 +6,9 @@ import { UserId } from "@bitwarden/common/types/guid";
import { WindowMain } from "../../../main/window.main";
import { BiometricsServiceAbstraction, OsBiometricService } from "./biometrics.service.abstraction";
import { DesktopBiometricsService, OsBiometricService } from "./desktop.biometrics.service";
export class BiometricsService implements BiometricsServiceAbstraction {
export class BiometricsService extends DesktopBiometricsService {
private platformSpecificService: OsBiometricService;
private clientKeyHalves = new Map<string, string>();
@@ -20,6 +20,7 @@ export class BiometricsService implements BiometricsServiceAbstraction {
private platform: NodeJS.Platform,
private biometricStateService: BiometricStateService,
) {
super();
this.loadPlatformSpecificService(this.platform);
}
@@ -63,19 +64,19 @@ export class BiometricsService implements BiometricsServiceAbstraction {
this.platformSpecificService = new NoopBiometricsService();
}
async osSupportsBiometric() {
async supportsBiometric() {
return await this.platformSpecificService.osSupportsBiometric();
}
async osBiometricsNeedsSetup() {
async biometricsNeedsSetup() {
return await this.platformSpecificService.osBiometricsNeedsSetup();
}
async osBiometricsCanAutoSetup() {
async biometricsSupportsAutoSetup() {
return await this.platformSpecificService.osBiometricsCanAutoSetup();
}
async osBiometricsSetup() {
async biometricsSetup() {
await this.platformSpecificService.osBiometricsSetup();
}
@@ -91,7 +92,7 @@ export class BiometricsService implements BiometricsServiceAbstraction {
const requireClientKeyHalf = await this.biometricStateService.getRequirePasswordOnStart(userId);
const clientKeyHalfB64 = this.getClientKeyHalf(service, key);
const clientKeyHalfSatisfied = !requireClientKeyHalf || !!clientKeyHalfB64;
return clientKeyHalfSatisfied && (await this.osSupportsBiometric());
return clientKeyHalfSatisfied && (await this.supportsBiometric());
}
async authenticateBiometric(): Promise<boolean> {
@@ -110,6 +111,10 @@ export class BiometricsService implements BiometricsServiceAbstraction {
return result;
}
async isBiometricUnlockAvailable(): Promise<boolean> {
return await this.platformSpecificService.osSupportsBiometric();
}
async getBiometricKey(service: string, storageKey: string): Promise<string | null> {
return await this.interruptProcessReload(async () => {
await this.enforceClientKeyHalf(service, storageKey);

View File

@@ -1,8 +1,10 @@
export abstract class BiometricsServiceAbstraction {
abstract osSupportsBiometric(): Promise<boolean>;
abstract osBiometricsNeedsSetup: () => Promise<boolean>;
abstract osBiometricsCanAutoSetup: () => Promise<boolean>;
abstract osBiometricsSetup: () => Promise<void>;
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
/**
* This service extends the base biometrics service to provide desktop specific functions,
* specifically for the main process.
*/
export abstract class DesktopBiometricsService extends BiometricsService {
abstract canAuthBiometric({
service,
key,
@@ -12,7 +14,6 @@ export abstract class BiometricsServiceAbstraction {
key: string;
userId: string;
}): Promise<boolean>;
abstract authenticateBiometric(): Promise<boolean>;
abstract getBiometricKey(service: string, key: string): Promise<string | null>;
abstract setBiometricKey(service: string, key: string, value: string): Promise<void>;
abstract setEncryptionKeyHalf({

View File

@@ -1,2 +1,2 @@
export * from "./biometrics.service.abstraction";
export * from "./desktop.biometrics.service";
export * from "./biometrics.service";

View File

@@ -6,14 +6,14 @@ import { passwords } from "@bitwarden/desktop-napi";
import { BiometricMessage, BiometricAction } from "../../types/biometric-message";
import { BiometricsServiceAbstraction } from "./biometric/index";
import { DesktopBiometricsService } from "./biometric/index";
const AuthRequiredSuffix = "_biometric";
export class DesktopCredentialStorageListener {
constructor(
private serviceName: string,
private biometricService: BiometricsServiceAbstraction,
private biometricService: DesktopBiometricsService,
private logService: ConsoleLogService,
) {}
@@ -77,16 +77,16 @@ export class DesktopCredentialStorageListener {
});
break;
case BiometricAction.OsSupported:
val = await this.biometricService.osSupportsBiometric();
val = await this.biometricService.supportsBiometric();
break;
case BiometricAction.NeedsSetup:
val = await this.biometricService.osBiometricsNeedsSetup();
val = await this.biometricService.biometricsNeedsSetup();
break;
case BiometricAction.Setup:
await this.biometricService.osBiometricsSetup();
await this.biometricService.biometricsSetup();
break;
case BiometricAction.CanAutoSetup:
val = await this.biometricService.osBiometricsCanAutoSetup();
val = await this.biometricService.biometricsSupportsAutoSetup();
break;
default:
}

View File

@@ -0,0 +1,38 @@
import { Injectable } from "@angular/core";
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
/**
* This service implement the base biometrics service to provide desktop specific functions,
* specifically for the renderer process by passing messages to the main process.
*/
@Injectable()
export class ElectronBiometricsService extends BiometricsService {
async supportsBiometric(): Promise<boolean> {
return await ipc.platform.biometric.osSupported();
}
async isBiometricUnlockAvailable(): Promise<boolean> {
return await ipc.platform.biometric.osSupported();
}
/** This method is used to authenticate the user presence _only_.
* It should not be used in the process to retrieve
* biometric keys, which has a separate authentication mechanism.
* For biometric keys, invoke "keytar" with a biometric key suffix */
async authenticateBiometric(): Promise<boolean> {
return await ipc.platform.biometric.authenticate();
}
async biometricsNeedsSetup(): Promise<boolean> {
return await ipc.platform.biometric.biometricsNeedsSetup();
}
async biometricsSupportsAutoSetup(): Promise<boolean> {
return await ipc.platform.biometric.biometricsCanAutoSetup();
}
async biometricsSetup(): Promise<void> {
return await ipc.platform.biometric.biometricsSetup();
}
}

View File

@@ -131,30 +131,6 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
return ipc.platform.clipboard.read();
}
async supportsBiometric(): Promise<boolean> {
return await ipc.platform.biometric.osSupported();
}
async biometricsNeedsSetup(): Promise<boolean> {
return await ipc.platform.biometric.biometricsNeedsSetup();
}
async biometricsSupportsAutoSetup(): Promise<boolean> {
return await ipc.platform.biometric.biometricsCanAutoSetup();
}
async biometricsSetup(): Promise<void> {
return await ipc.platform.biometric.biometricsSetup();
}
/** This method is used to authenticate the user presence _only_.
* It should not be used in the process to retrieve
* biometric keys, which has a separate authentication mechanism.
* For biometric keys, invoke "keytar" with a biometric key suffix */
async authenticateBiometric(): Promise<boolean> {
return await ipc.platform.biometric.authenticate();
}
supportsSecureStorage(): boolean {
return ELECTRON_SUPPORTS_SECURE_STORAGE;
}