1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 08:43:33 +00:00
Files
browser/apps/browser/src/key-management/biometrics/background-browser-biometrics.service.ts
Oscar Hinton 3c2a83fa81 Remove injectable from background-browser-biometrics (#15209)
Injectable is for angular, this only runs in the background.
2025-06-19 11:40:48 +02:00

140 lines
5.0 KiB
TypeScript

import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey } from "@bitwarden/common/types/key";
import {
BiometricsService,
BiometricsCommands,
BiometricsStatus,
KeyService,
BiometricStateService,
} from "@bitwarden/key-management";
import { NativeMessagingBackground } from "../../background/nativeMessaging.background";
import { BrowserApi } from "../../platform/browser/browser-api";
export class BackgroundBrowserBiometricsService extends BiometricsService {
constructor(
private nativeMessagingBackground: () => NativeMessagingBackground,
private logService: LogService,
private keyService: KeyService,
private biometricStateService: BiometricStateService,
private messagingService: MessagingService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
) {
super();
}
async authenticateWithBiometrics(): Promise<boolean> {
try {
await this.ensureConnected();
const response = await this.nativeMessagingBackground().callCommand({
command: BiometricsCommands.AuthenticateWithBiometrics,
});
return response.response;
} catch (e) {
this.logService.info("Biometric authentication failed", e);
return false;
}
}
async getBiometricsStatus(): Promise<BiometricsStatus> {
if (!(await BrowserApi.permissionsGranted(["nativeMessaging"]))) {
return BiometricsStatus.NativeMessagingPermissionMissing;
}
try {
await this.ensureConnected();
const response = await this.nativeMessagingBackground().callCommand({
command: BiometricsCommands.GetBiometricsStatus,
});
if (response.response) {
return response.response;
}
return BiometricsStatus.Available;
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return BiometricsStatus.DesktopDisconnected;
}
}
async unlockWithBiometricsForUser(userId: UserId): Promise<UserKey | null> {
try {
await this.ensureConnected();
const response = await this.nativeMessagingBackground().callCommand({
command: BiometricsCommands.UnlockWithBiometricsForUser,
userId: userId,
});
if (response.response) {
// In case the requesting foreground context dies (popup), the userkey should still be set, so the user is unlocked / the setting should be enabled
const decodedUserkey = Utils.fromB64ToArray(response.userKeyB64);
const userKey = new SymmetricCryptoKey(decodedUserkey) as UserKey;
if (await this.keyService.validateUserKey(userKey, userId)) {
await this.biometricStateService.setBiometricUnlockEnabled(true);
await this.keyService.setUserKey(userKey, userId);
// to update badge and other things
this.messagingService.send("switchAccount", { userId });
return userKey;
}
} else {
return null;
}
} catch (e) {
this.logService.info("Biometric unlock for user failed", e);
throw new Error("Biometric unlock failed");
}
return null;
}
async getBiometricsStatusForUser(id: UserId): Promise<BiometricsStatus> {
try {
await this.ensureConnected();
return (
await this.nativeMessagingBackground().callCommand({
command: BiometricsCommands.GetBiometricsStatusForUser,
userId: id,
})
).response;
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return BiometricsStatus.DesktopDisconnected;
}
}
// the first time we call, this might use an outdated version of the protocol, so we drop the response
private async ensureConnected() {
if (!this.nativeMessagingBackground().connected) {
await this.nativeMessagingBackground().callCommand({
command: BiometricsCommands.GetBiometricsStatus,
});
}
}
async getShouldAutopromptNow(): Promise<boolean> {
return false;
}
async setShouldAutopromptNow(value: boolean): Promise<void> {}
async canEnableBiometricUnlock(): Promise<boolean> {
const status = await this.getBiometricsStatus();
const isBiometricsAlreadyEnabled = await this.vaultTimeoutSettingsService.isBiometricLockSet();
const statusAllowsBiometric =
status !== BiometricsStatus.DesktopDisconnected &&
status !== BiometricsStatus.NotEnabledInConnectedDesktopApp &&
status !== BiometricsStatus.HardwareUnavailable;
return statusAllowsBiometric || isBiometricsAlreadyEnabled;
}
}