diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 7fc97a4004..4e3f7b7abc 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2018,12 +2018,6 @@ "biometricsNotUnlockedDesc": { "message": "Please unlock this user in the desktop application and try again." }, - "biometricsNotAvailableTitle": { - "message": "Biometric unlock unavailable" - }, - "biometricsNotAvailableDesc": { - "message": "Biometric unlock is currently unavailable. Please try again later." - }, "biometricsFailedTitle": { "message": "Biometrics failed" }, diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts index e9c29b8fbc..5047889b8e 100644 --- a/apps/browser/src/auth/popup/lock.component.ts +++ b/apps/browser/src/auth/popup/lock.component.ts @@ -24,7 +24,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService } from "@bitwarden/components"; @@ -68,7 +67,6 @@ export class LockComponent extends BaseLockComponent { pinService: PinServiceAbstraction, private routerService: BrowserRouterService, biometricStateService: BiometricStateService, - biometricsService: BiometricsService, accountService: AccountService, kdfConfigService: KdfConfigService, syncService: SyncService, @@ -95,7 +93,6 @@ export class LockComponent extends BaseLockComponent { userVerificationService, pinService, biometricStateService, - biometricsService, accountService, authService, kdfConfigService, @@ -132,35 +129,22 @@ export class LockComponent extends BaseLockComponent { this.isInitialLockScreen && (await this.authService.getAuthStatus()) === AuthenticationStatus.Locked ) { - await this.unlockBiometric(true); + await this.unlockBiometric(); } }, 100); } - override async unlockBiometric(automaticPrompt: boolean = false): Promise { + override async unlockBiometric(): Promise { if (!this.biometricLock) { return; } + this.pendingBiometric = true; this.biometricError = null; let success; try { - const available = await super.isBiometricUnlockAvailable(); - if (!available) { - if (!automaticPrompt) { - await this.dialogService.openSimpleDialog({ - type: "warning", - title: { key: "biometricsNotAvailableTitle" }, - content: { key: "biometricsNotAvailableDesc" }, - acceptButtonText: { key: "ok" }, - cancelButtonText: null, - }); - } - } else { - this.pendingBiometric = true; - success = await super.unlockBiometric(); - } + success = await super.unlockBiometric(); } catch (e) { const error = BiometricErrors[e?.message as BiometricErrorTypes]; diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index c35f7c0242..10c9b2fb98 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -32,7 +32,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { VaultTimeout, VaultTimeoutOption, @@ -94,7 +93,6 @@ export class AccountSecurityComponent implements OnInit { private dialogService: DialogService, private changeDetectorRef: ChangeDetectorRef, private biometricStateService: BiometricStateService, - private biometricsService: BiometricsService, ) { this.accountSwitcherEnabled = enableAccountSwitching(); } @@ -166,7 +164,7 @@ export class AccountSecurityComponent implements OnInit { }; this.form.patchValue(initialValues, { emitEvent: false }); - this.supportsBiometric = await this.biometricsService.supportsBiometric(); + this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.showChangeMasterPass = await this.userVerificationService.hasMasterPassword(); this.form.controls.vaultTimeout.valueChanges @@ -395,7 +393,7 @@ export class AccountSecurityComponent implements OnInit { this.form.controls.biometric.setValue(false); } }), - this.biometricsService + this.platformUtilsService .authenticateBiometric() .then((result) => { this.form.controls.biometric.setValue(result); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 1bd70e4eb1..70978f4070 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -97,7 +97,6 @@ import { BiometricStateService, DefaultBiometricStateService, } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency creation @@ -224,7 +223,6 @@ import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender import { OffscreenDocumentService } from "../platform/offscreen-document/abstractions/offscreen-document"; import { DefaultOffscreenDocumentService } from "../platform/offscreen-document/offscreen-document.service"; import { BrowserTaskSchedulerService } from "../platform/services/abstractions/browser-task-scheduler.service"; -import { BackgroundBrowserBiometricsService } from "../platform/services/background-browser-biometrics."; import { BrowserCryptoService } from "../platform/services/browser-crypto.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import BrowserLocalStorageService from "../platform/services/browser-local-storage.service"; @@ -339,7 +337,6 @@ export default class MainBackground { organizationVaultExportService: OrganizationVaultExportServiceAbstraction; vaultSettingsService: VaultSettingsServiceAbstraction; biometricStateService: BiometricStateService; - biometricsService: BiometricsService; stateEventRunnerService: StateEventRunnerService; ssoLoginService: SsoLoginServiceAbstraction; billingAccountProfileStateService: BillingAccountProfileStateService; @@ -423,6 +420,7 @@ export default class MainBackground { this.platformUtilsService = new BackgroundPlatformUtilsService( this.messagingService, (clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs), + async () => this.biometricUnlock(), self, this.offscreenDocumentService, ); @@ -591,8 +589,6 @@ export default class MainBackground { this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider); - this.biometricsService = new BackgroundBrowserBiometricsService(this.nativeMessagingBackground); - this.kdfConfigService = new KdfConfigService(this.stateProvider); this.pinService = new PinService( @@ -619,7 +615,6 @@ export default class MainBackground { this.accountService, this.stateProvider, this.biometricStateService, - this.biometricsService, this.kdfConfigService, ); @@ -1468,6 +1463,17 @@ export default class MainBackground { } } + async biometricUnlock(): Promise { + if (this.nativeMessagingBackground == null) { + return false; + } + + const responsePromise = this.nativeMessagingBackground.getResponse(); + await this.nativeMessagingBackground.send({ command: "biometricUnlock" }); + const response = await responsePromise; + return response.response === "unlocked"; + } + private async fullSync(override = false) { const syncInternal = 6 * 60 * 60 * 1000; // 6 hours const lastSync = await this.syncService.getLastSync(); diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 1f8fe82859..3fb943f613 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -326,15 +326,6 @@ export class NativeMessagingBackground { type: "danger", }); break; - } else if (message.response === "not available") { - this.messagingService.send("showDialog", { - title: { key: "biometricsNotAvailableTitle" }, - content: { key: "biometricsNotAvailableDesc" }, - acceptButtonText: { key: "ok" }, - cancelButtonText: null, - type: "danger", - }); - break; } else if (message.response === "canceled") { break; } @@ -401,10 +392,6 @@ export class NativeMessagingBackground { } break; } - case "biometricUnlockAvailable": { - this.resolver(message); - break; - } default: this.logService.error("NativeMessage, got unknown command: " + message.command); break; diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index dbdca8c1de..8b216b9a67 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -68,7 +68,6 @@ export default class RuntimeBackground { ) => { const messagesWithResponse = [ "biometricUnlock", - "biometricUnlockAvailable", "getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag", "getInlineMenuFieldQualificationFeatureFlag", ]; @@ -180,11 +179,7 @@ export default class RuntimeBackground { } break; case "biometricUnlock": { - const result = await this.main.biometricsService.authenticateBiometric(); - return result; - } - case "biometricUnlockAvailable": { - const result = await this.main.biometricsService.isBiometricUnlockAvailable(); + const result = await this.main.biometricUnlock(); return result; } case "getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag": { diff --git a/apps/browser/src/platform/services/background-browser-biometrics..ts b/apps/browser/src/platform/services/background-browser-biometrics..ts deleted file mode 100644 index cf79f4f555..0000000000 --- a/apps/browser/src/platform/services/background-browser-biometrics..ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { NativeMessagingBackground } from "../../background/nativeMessaging.background"; - -import { BrowserBiometricsService } from "./browser-biometrics.service"; - -@Injectable() -export class BackgroundBrowserBiometricsService extends BrowserBiometricsService { - constructor(private nativeMessagingBackground: NativeMessagingBackground) { - super(); - } - - async authenticateBiometric(): Promise { - const responsePromise = this.nativeMessagingBackground.getResponse(); - await this.nativeMessagingBackground.send({ command: "biometricUnlock" }); - const response = await responsePromise; - return response.response === "unlocked"; - } - - async isBiometricUnlockAvailable(): Promise { - const responsePromise = this.nativeMessagingBackground.getResponse(); - await this.nativeMessagingBackground.send({ command: "biometricUnlockAvailable" }); - const response = await responsePromise; - return response.response === "available"; - } -} diff --git a/apps/browser/src/platform/services/browser-biometrics.service.ts b/apps/browser/src/platform/services/browser-biometrics.service.ts deleted file mode 100644 index a5b72b9293..0000000000 --- a/apps/browser/src/platform/services/browser-biometrics.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; - -import { BrowserApi } from "../browser/browser-api"; - -@Injectable() -export abstract class BrowserBiometricsService extends BiometricsService { - async supportsBiometric() { - const platformInfo = await BrowserApi.getPlatformInfo(); - if (platformInfo.os === "mac" || platformInfo.os === "win") { - return true; - } - return false; - } - - abstract authenticateBiometric(): Promise; - abstract isBiometricUnlockAvailable(): Promise; -} diff --git a/apps/browser/src/platform/services/browser-crypto.service.ts b/apps/browser/src/platform/services/browser-crypto.service.ts index 1d61fb4c8e..1242d52021 100644 --- a/apps/browser/src/platform/services/browser-crypto.service.ts +++ b/apps/browser/src/platform/services/browser-crypto.service.ts @@ -11,7 +11,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; import { USER_KEY } from "@bitwarden/common/platform/services/key-state/user-key.state"; @@ -32,7 +31,6 @@ export class BrowserCryptoService extends CryptoService { accountService: AccountService, stateProvider: StateProvider, private biometricStateService: BiometricStateService, - private biometricsService: BiometricsService, kdfConfigService: KdfConfigService, ) { super( @@ -70,7 +68,7 @@ export class BrowserCryptoService extends CryptoService { userId?: UserId, ): Promise { if (keySuffix === KeySuffixOptions.Biometric) { - const biometricsResult = await this.biometricsService.authenticateBiometric(); + const biometricsResult = await this.platformUtilService.authenticateBiometric(); if (!biometricsResult) { return null; diff --git a/apps/browser/src/platform/services/foreground-browser-biometrics.ts b/apps/browser/src/platform/services/foreground-browser-biometrics.ts deleted file mode 100644 index 060308ee24..0000000000 --- a/apps/browser/src/platform/services/foreground-browser-biometrics.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { BrowserApi } from "../browser/browser-api"; - -import { BrowserBiometricsService } from "./browser-biometrics.service"; - -export class ForegroundBrowserBiometricsService extends BrowserBiometricsService { - async authenticateBiometric(): Promise { - const response = await BrowserApi.sendMessageWithResponse<{ - result: boolean; - error: string; - }>("biometricUnlock"); - if (!response.result) { - throw response.error; - } - return response.result; - } - - async isBiometricUnlockAvailable(): Promise { - const response = await BrowserApi.sendMessageWithResponse<{ - result: boolean; - error: string; - }>("biometricUnlockAvailable"); - return response.result && response.result === true; - } -} diff --git a/apps/browser/src/platform/services/platform-utils/background-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/background-platform-utils.service.ts index da6a8faf3e..ec26d6aa29 100644 --- a/apps/browser/src/platform/services/platform-utils/background-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/background-platform-utils.service.ts @@ -8,10 +8,11 @@ export class BackgroundPlatformUtilsService extends BrowserPlatformUtilsService constructor( private messagingService: MessagingService, clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, + biometricCallback: () => Promise, win: Window & typeof globalThis, offscreenDocumentService: OffscreenDocumentService, ) { - super(clipboardWriteCallback, win, offscreenDocumentService); + super(clipboardWriteCallback, biometricCallback, win, offscreenDocumentService); } override showToast( diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts index 762380071b..c86c915801 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.spec.ts @@ -16,7 +16,7 @@ class TestBrowserPlatformUtilsService extends BrowserPlatformUtilsService { win: Window & typeof globalThis, offscreenDocumentService: OffscreenDocumentService, ) { - super(clipboardSpy, win, offscreenDocumentService); + super(clipboardSpy, null, win, offscreenDocumentService); } showToast( diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts index b47488bdd7..047687e09f 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts @@ -15,6 +15,7 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic constructor( private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, + private biometricCallback: () => Promise, private globalContext: Window | ServiceWorkerGlobalScope, private offscreenDocumentService: OffscreenDocumentService, ) {} @@ -275,6 +276,18 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic return await BrowserClipboardService.read(windowContext); } + async supportsBiometric() { + const platformInfo = await BrowserApi.getPlatformInfo(); + if (platformInfo.os === "mac" || platformInfo.os === "win") { + return true; + } + return false; + } + + authenticateBiometric() { + return this.biometricCallback(); + } + supportsSecureStorage(): boolean { return false; } diff --git a/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts index 5b4b7288d1..f775f049e7 100644 --- a/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/foreground-platform-utils.service.ts @@ -8,10 +8,11 @@ export class ForegroundPlatformUtilsService extends BrowserPlatformUtilsService constructor( private toastService: ToastService, clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, + biometricCallback: () => Promise, win: Window & typeof globalThis, offscreenDocumentService: OffscreenDocumentService, ) { - super(clipboardWriteCallback, win, offscreenDocumentService); + super(clipboardWriteCallback, biometricCallback, win, offscreenDocumentService); } override showToast( diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index f34407e0d1..2c7129db29 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -57,7 +57,6 @@ import { ObservableStorageService, } from "@bitwarden/common/platform/abstractions/storage.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency injection import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; @@ -100,7 +99,6 @@ import { BrowserCryptoService } from "../../platform/services/browser-crypto.ser import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service"; import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service"; import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; -import { ForegroundBrowserBiometricsService } from "../../platform/services/foreground-browser-biometrics"; import I18nService from "../../platform/services/i18n.service"; import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service"; import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service"; @@ -205,7 +203,6 @@ const safeProviders: SafeProvider[] = [ accountService: AccountServiceAbstraction, stateProvider: StateProvider, biometricStateService: BiometricStateService, - biometricsService: BiometricsService, kdfConfigService: KdfConfigService, ) => { const cryptoService = new BrowserCryptoService( @@ -220,7 +217,6 @@ const safeProviders: SafeProvider[] = [ accountService, stateProvider, biometricStateService, - biometricsService, kdfConfigService, ); new ContainerService(cryptoService, encryptService).attachToGlobal(self); @@ -238,7 +234,6 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, StateProvider, BiometricStateService, - BiometricsService, KdfConfigService, ], }), @@ -263,19 +258,22 @@ const safeProviders: SafeProvider[] = [ (clipboardValue: string, clearMs: number) => { void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs }); }, + async () => { + const response = await BrowserApi.sendMessageWithResponse<{ + result: boolean; + error: string; + }>("biometricUnlock"); + if (!response.result) { + throw response.error; + } + return response.result; + }, window, offscreenDocumentService, ); }, deps: [ToastService, OffscreenDocumentService], }), - safeProvider({ - provide: BiometricsService, - useFactory: () => { - return new ForegroundBrowserBiometricsService(); - }, - deps: [], - }), safeProvider({ provide: SyncService, useFactory: getBgService("syncService"), diff --git a/apps/cli/src/platform/services/cli-platform-utils.service.ts b/apps/cli/src/platform/services/cli-platform-utils.service.ts index 24bceec389..0950a7dfec 100644 --- a/apps/cli/src/platform/services/cli-platform-utils.service.ts +++ b/apps/cli/src/platform/services/cli-platform-utils.service.ts @@ -131,6 +131,14 @@ export class CliPlatformUtilsService implements PlatformUtilsService { throw new Error("Not implemented."); } + supportsBiometric(): Promise { + return Promise.resolve(false); + } + + authenticateBiometric(): Promise { + return Promise.resolve(false); + } + supportsSecureStorage(): boolean { return false; } diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index a9fafbaf1b..4f006f2364 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -20,7 +20,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { KeySuffixOptions, ThemeType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; @@ -133,7 +132,6 @@ export class SettingsComponent implements OnInit { private userVerificationService: UserVerificationServiceAbstraction, private desktopSettingsService: DesktopSettingsService, private biometricStateService: BiometricStateService, - private biometricsService: BiometricsService, private desktopAutofillSettingsService: DesktopAutofillSettingsService, private pinService: PinServiceAbstraction, private logService: LogService, @@ -287,7 +285,7 @@ export class SettingsComponent implements OnInit { // Non-form values this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop; this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; - this.supportsBiometric = await this.biometricsService.supportsBiometric(); + this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.previousVaultTimeout = this.form.value.vaultTimeout; this.refreshTimeoutSettings$ diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index be110be138..85bfbc09f6 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -56,7 +56,6 @@ import { StateService as StateServiceAbstraction } from "@bitwarden/common/platf import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency injection import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; @@ -73,7 +72,6 @@ import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legac import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; -import { ElectronBiometricsService } from "../../platform/services/electron-biometrics.service"; import { ElectronCryptoService } from "../../platform/services/electron-crypto.service"; import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service"; import { @@ -106,11 +104,6 @@ const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK"); */ const safeProviders: SafeProvider[] = [ safeProvider(InitService), - safeProvider({ - provide: BiometricsService, - useClass: ElectronBiometricsService, - deps: [], - }), safeProvider(NativeMessagingService), safeProvider(SearchBarService), safeProvider(DialogService), diff --git a/apps/desktop/src/auth/lock.component.spec.ts b/apps/desktop/src/auth/lock.component.spec.ts index c5b5b7acf0..c46b791b1b 100644 --- a/apps/desktop/src/auth/lock.component.spec.ts +++ b/apps/desktop/src/auth/lock.component.spec.ts @@ -28,7 +28,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService as AbstractBiometricService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; @@ -36,8 +35,6 @@ import { UserId } from "@bitwarden/common/types/guid"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService } from "@bitwarden/components"; -import { BiometricsService } from "src/platform/main/biometric"; - import { LockComponent } from "./lock.component"; // ipc mock global @@ -56,7 +53,6 @@ describe("LockComponent", () => { let fixture: ComponentFixture; let stateServiceMock: MockProxy; let biometricStateService: MockProxy; - let biometricsService: MockProxy; let messagingServiceMock: MockProxy; let broadcasterServiceMock: MockProxy; let platformUtilsServiceMock: MockProxy; @@ -167,10 +163,6 @@ describe("LockComponent", () => { provide: BiometricStateService, useValue: biometricStateService, }, - { - provide: AbstractBiometricService, - useValue: biometricsService, - }, { provide: AccountService, useValue: accountService, diff --git a/apps/desktop/src/auth/lock.component.ts b/apps/desktop/src/auth/lock.component.ts index 518e2a8189..2a71a3d693 100644 --- a/apps/desktop/src/auth/lock.component.ts +++ b/apps/desktop/src/auth/lock.component.ts @@ -25,7 +25,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService } from "@bitwarden/components"; @@ -67,7 +66,6 @@ export class LockComponent extends BaseLockComponent { userVerificationService: UserVerificationService, pinService: PinServiceAbstraction, biometricStateService: BiometricStateService, - biometricsService: BiometricsService, accountService: AccountService, authService: AuthService, kdfConfigService: KdfConfigService, @@ -95,7 +93,6 @@ export class LockComponent extends BaseLockComponent { userVerificationService, pinService, biometricStateService, - biometricsService, accountService, authService, kdfConfigService, diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index ced82d1421..816d317f41 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -32,7 +32,7 @@ import { PowerMonitorMain } from "./main/power-monitor.main"; import { TrayMain } from "./main/tray.main"; import { UpdaterMain } from "./main/updater.main"; import { WindowMain } from "./main/window.main"; -import { BiometricsService, DesktopBiometricsService } from "./platform/main/biometric/index"; +import { BiometricsService, BiometricsServiceAbstraction } from "./platform/main/biometric/index"; import { ClipboardMain } from "./platform/main/clipboard.main"; import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener"; import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service"; @@ -63,7 +63,7 @@ export class Main { menuMain: MenuMain; powerMonitorMain: PowerMonitorMain; trayMain: TrayMain; - biometricsService: DesktopBiometricsService; + biometricsService: BiometricsServiceAbstraction; nativeMessagingMain: NativeMessagingMain; clipboardMain: ClipboardMain; desktopAutofillSettingsService: DesktopAutofillSettingsService; diff --git a/apps/desktop/src/platform/main/biometric/biometric.darwin.main.ts b/apps/desktop/src/platform/main/biometric/biometric.darwin.main.ts index 83a1af571d..e1a5c3da9a 100644 --- a/apps/desktop/src/platform/main/biometric/biometric.darwin.main.ts +++ b/apps/desktop/src/platform/main/biometric/biometric.darwin.main.ts @@ -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 "./desktop.biometrics.service"; +import { OsBiometricService } from "./biometrics.service.abstraction"; export default class BiometricDarwinMain implements OsBiometricService { constructor(private i18nservice: I18nService) {} diff --git a/apps/desktop/src/platform/main/biometric/biometric.noop.main.ts b/apps/desktop/src/platform/main/biometric/biometric.noop.main.ts index fe26f15b40..152b5ce32f 100644 --- a/apps/desktop/src/platform/main/biometric/biometric.noop.main.ts +++ b/apps/desktop/src/platform/main/biometric/biometric.noop.main.ts @@ -1,4 +1,4 @@ -import { OsBiometricService } from "./desktop.biometrics.service"; +import { OsBiometricService } from "./biometrics.service.abstraction"; export default class NoopBiometricsService implements OsBiometricService { constructor() {} diff --git a/apps/desktop/src/platform/main/biometric/biometric.windows.main.ts b/apps/desktop/src/platform/main/biometric/biometric.windows.main.ts index c7f703629c..75d5bce8f5 100644 --- a/apps/desktop/src/platform/main/biometric/biometric.windows.main.ts +++ b/apps/desktop/src/platform/main/biometric/biometric.windows.main.ts @@ -6,7 +6,7 @@ import { biometrics, passwords } from "@bitwarden/desktop-napi"; import { WindowMain } from "../../../main/window.main"; -import { OsBiometricService } from "./desktop.biometrics.service"; +import { OsBiometricService } from "./biometrics.service.abstraction"; const KEY_WITNESS_SUFFIX = "_witness"; const WITNESS_VALUE = "known key"; diff --git a/apps/desktop/src/platform/main/biometric/desktop.biometrics.service.ts b/apps/desktop/src/platform/main/biometric/biometrics.service.abstraction.ts similarity index 76% rename from apps/desktop/src/platform/main/biometric/desktop.biometrics.service.ts rename to apps/desktop/src/platform/main/biometric/biometrics.service.abstraction.ts index 71dfb6b02e..fb7ce048b5 100644 --- a/apps/desktop/src/platform/main/biometric/desktop.biometrics.service.ts +++ b/apps/desktop/src/platform/main/biometric/biometrics.service.abstraction.ts @@ -1,10 +1,5 @@ -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 { +export abstract class BiometricsServiceAbstraction { + abstract osSupportsBiometric(): Promise; abstract canAuthBiometric({ service, key, @@ -14,6 +9,7 @@ export abstract class DesktopBiometricsService extends BiometricsService { key: string; userId: string; }): Promise; + abstract authenticateBiometric(): Promise; abstract getBiometricKey(service: string, key: string): Promise; abstract setBiometricKey(service: string, key: string, value: string): Promise; abstract setEncryptionKeyHalf({ diff --git a/apps/desktop/src/platform/main/biometric/biometrics.service.spec.ts b/apps/desktop/src/platform/main/biometric/biometrics.service.spec.ts index 10ba1c83b6..cb6bb4858c 100644 --- a/apps/desktop/src/platform/main/biometric/biometrics.service.spec.ts +++ b/apps/desktop/src/platform/main/biometric/biometrics.service.spec.ts @@ -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 "./desktop.biometrics.service"; +import { OsBiometricService } from "./biometrics.service.abstraction"; jest.mock("@bitwarden/desktop-napi", () => { return { diff --git a/apps/desktop/src/platform/main/biometric/biometrics.service.ts b/apps/desktop/src/platform/main/biometric/biometrics.service.ts index 470e45a83a..b0331cce3e 100644 --- a/apps/desktop/src/platform/main/biometric/biometrics.service.ts +++ b/apps/desktop/src/platform/main/biometric/biometrics.service.ts @@ -6,9 +6,9 @@ import { UserId } from "@bitwarden/common/types/guid"; import { WindowMain } from "../../../main/window.main"; -import { DesktopBiometricsService, OsBiometricService } from "./desktop.biometrics.service"; +import { BiometricsServiceAbstraction, OsBiometricService } from "./biometrics.service.abstraction"; -export class BiometricsService extends DesktopBiometricsService { +export class BiometricsService implements BiometricsServiceAbstraction { private platformSpecificService: OsBiometricService; private clientKeyHalves = new Map(); @@ -20,7 +20,6 @@ export class BiometricsService extends DesktopBiometricsService { private platform: NodeJS.Platform, private biometricStateService: BiometricStateService, ) { - super(); this.loadPlatformSpecificService(this.platform); } @@ -56,7 +55,7 @@ export class BiometricsService extends DesktopBiometricsService { this.platformSpecificService = new NoopBiometricsService(); } - async supportsBiometric() { + async osSupportsBiometric() { return await this.platformSpecificService.osSupportsBiometric(); } @@ -72,7 +71,7 @@ export class BiometricsService extends DesktopBiometricsService { const requireClientKeyHalf = await this.biometricStateService.getRequirePasswordOnStart(userId); const clientKeyHalfB64 = this.getClientKeyHalf(service, key); const clientKeyHalfSatisfied = !requireClientKeyHalf || !!clientKeyHalfB64; - return clientKeyHalfSatisfied && (await this.supportsBiometric()); + return clientKeyHalfSatisfied && (await this.osSupportsBiometric()); } async authenticateBiometric(): Promise { @@ -91,10 +90,6 @@ export class BiometricsService extends DesktopBiometricsService { return result; } - async isBiometricUnlockAvailable(): Promise { - return await this.platformSpecificService.osSupportsBiometric(); - } - async getBiometricKey(service: string, storageKey: string): Promise { return await this.interruptProcessReload(async () => { await this.enforceClientKeyHalf(service, storageKey); diff --git a/apps/desktop/src/platform/main/biometric/index.ts b/apps/desktop/src/platform/main/biometric/index.ts index ad7725d718..f5a594d966 100644 --- a/apps/desktop/src/platform/main/biometric/index.ts +++ b/apps/desktop/src/platform/main/biometric/index.ts @@ -1,2 +1,2 @@ -export * from "./desktop.biometrics.service"; +export * from "./biometrics.service.abstraction"; export * from "./biometrics.service"; diff --git a/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts b/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts index 9e4d39da1f..adc7935e05 100644 --- a/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts +++ b/apps/desktop/src/platform/main/desktop-credential-storage-listener.ts @@ -6,14 +6,14 @@ import { passwords } from "@bitwarden/desktop-napi"; import { BiometricMessage, BiometricAction } from "../../types/biometric-message"; -import { DesktopBiometricsService } from "./biometric/index"; +import { BiometricsServiceAbstraction } from "./biometric/index"; const AuthRequiredSuffix = "_biometric"; export class DesktopCredentialStorageListener { constructor( private serviceName: string, - private biometricService: DesktopBiometricsService, + private biometricService: BiometricsServiceAbstraction, private logService: ConsoleLogService, ) {} @@ -77,7 +77,7 @@ export class DesktopCredentialStorageListener { }); break; case BiometricAction.OsSupported: - val = await this.biometricService.supportsBiometric(); + val = await this.biometricService.osSupportsBiometric(); break; default: } diff --git a/apps/desktop/src/platform/services/electron-biometrics.service.ts b/apps/desktop/src/platform/services/electron-biometrics.service.ts deleted file mode 100644 index d0934282be..0000000000 --- a/apps/desktop/src/platform/services/electron-biometrics.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -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 { - return await ipc.platform.biometric.osSupported(); - } - - async isBiometricUnlockAvailable(): Promise { - 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 { - return await ipc.platform.biometric.authenticate(); - } -} diff --git a/apps/desktop/src/platform/services/electron-platform-utils.service.ts b/apps/desktop/src/platform/services/electron-platform-utils.service.ts index 2808b74f09..2d50712dfb 100644 --- a/apps/desktop/src/platform/services/electron-platform-utils.service.ts +++ b/apps/desktop/src/platform/services/electron-platform-utils.service.ts @@ -131,6 +131,18 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { return ipc.platform.clipboard.read(); } + async supportsBiometric(): Promise { + 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 { + return await ipc.platform.biometric.authenticate(); + } + supportsSecureStorage(): boolean { return ELECTRON_SUPPORTS_SECURE_STORAGE; } diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 2575c489c5..5980fade83 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -7,8 +7,8 @@ import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/c import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; @@ -32,11 +32,11 @@ export class NativeMessagingService { constructor( private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService, + private platformUtilService: PlatformUtilsService, private logService: LogService, private messagingService: MessagingService, private desktopSettingService: DesktopSettingsService, private biometricStateService: BiometricStateService, - private biometricsService: BiometricsService, private nativeMessageHandler: NativeMessageHandlerService, private dialogService: DialogService, private accountService: AccountService, @@ -132,14 +132,7 @@ export class NativeMessagingService { switch (message.command) { case "biometricUnlock": { - const isTemporarilyDisabled = - (await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) && - !(await this.biometricsService.supportsBiometric()); - if (isTemporarilyDisabled) { - return this.send({ command: "biometricUnlock", response: "not available" }, appId); - } - - if (!(await this.biometricsService.supportsBiometric())) { + if (!(await this.platformUtilService.supportsBiometric())) { return this.send({ command: "biometricUnlock", response: "not supported" }, appId); } @@ -194,18 +187,8 @@ export class NativeMessagingService { break; } - case "biometricUnlockAvailable": { - const isAvailable = await this.biometricsService.supportsBiometric(); - return this.send( - { - command: "biometricUnlockAvailable", - response: isAvailable ? "available" : "not available", - }, - appId, - ); - } default: - this.logService.error("NativeMessage, got unknown command: " + message.command); + this.logService.error("NativeMessage, got unknown command."); break; } } diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts index dbd0ef593d..02c7c29e34 100644 --- a/apps/web/src/app/core/web-platform-utils.service.ts +++ b/apps/web/src/app/core/web-platform-utils.service.ts @@ -186,6 +186,14 @@ export class WebPlatformUtilsService implements PlatformUtilsService { throw new Error("Cannot read from clipboard on web."); } + supportsBiometric() { + return Promise.resolve(false); + } + + authenticateBiometric() { + return Promise.resolve(false); + } + supportsSecureStorage() { return false; } diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts index 5e14971c7b..88b042c5b8 100644 --- a/libs/angular/src/auth/components/lock.component.ts +++ b/libs/angular/src/auth/components/lock.component.ts @@ -32,7 +32,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; -import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service"; import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { UserId } from "@bitwarden/common/types/guid"; @@ -87,7 +86,6 @@ export class LockComponent implements OnInit, OnDestroy { protected userVerificationService: UserVerificationService, protected pinService: PinServiceAbstraction, protected biometricStateService: BiometricStateService, - protected biometricsService: BiometricsService, protected accountService: AccountService, protected authService: AuthService, protected kdfConfigService: KdfConfigService, @@ -147,13 +145,6 @@ export class LockComponent implements OnInit, OnDestroy { return !!userKey; } - async isBiometricUnlockAvailable(): Promise { - if (!(await this.biometricsService.supportsBiometric())) { - return false; - } - return this.biometricsService.isBiometricUnlockAvailable(); - } - togglePassword() { this.showPassword = !this.showPassword; const input = document.getElementById(this.pinEnabled ? "pin" : "masterPassword"); @@ -364,7 +355,7 @@ export class LockComponent implements OnInit, OnDestroy { this.masterPasswordEnabled = await this.userVerificationService.hasMasterPassword(); - this.supportsBiometric = await this.biometricsService.supportsBiometric(); + this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.biometricLock = (await this.vaultTimeoutSettingsService.isBiometricLockSet()) && ((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) || diff --git a/libs/common/src/platform/abstractions/platform-utils.service.ts b/libs/common/src/platform/abstractions/platform-utils.service.ts index fa0fc8f250..f2dff46c78 100644 --- a/libs/common/src/platform/abstractions/platform-utils.service.ts +++ b/libs/common/src/platform/abstractions/platform-utils.service.ts @@ -43,6 +43,8 @@ export abstract class PlatformUtilsService { abstract isSelfHost(): boolean; abstract copyToClipboard(text: string, options?: ClipboardOptions): void | boolean; abstract readFromClipboard(): Promise; + abstract supportsBiometric(): Promise; + abstract authenticateBiometric(): Promise; abstract supportsSecureStorage(): boolean; abstract getAutofillKeyboardShortcut(): Promise; } diff --git a/libs/common/src/platform/biometrics/biometric.service.ts b/libs/common/src/platform/biometrics/biometric.service.ts deleted file mode 100644 index d2056c6bf9..0000000000 --- a/libs/common/src/platform/biometrics/biometric.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * The biometrics service is used to provide access to the status of and access to biometric functionality on the platforms. - */ -export abstract class BiometricsService { - /** - * Check if the platform supports biometric authentication. - */ - abstract supportsBiometric(): Promise; - - /** - * Checks whether biometric unlock is currently available at the moment (e.g. if the laptop lid is shut, biometric unlock may not be available) - */ - abstract isBiometricUnlockAvailable(): Promise; - - /** - * Performs biometric authentication - */ - abstract authenticateBiometric(): Promise; -}