1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-04 09:33:27 +00:00
Files
browser/apps/desktop/src/key-management/biometrics/os-biometrics-mac.service.ts
Thomas Avery f65e5d52c2 [PM-25373] Windows native biometric rewrite (#16432)
* Extract windows biometrics v2 changes

Co-authored-by: Bernd Schoolmann <mail@quexten.com>

* Handle TDE edge cases

* Make windows rust code async and fix restoring focus freezes

* Add unit test coverage

---------

Co-authored-by: Bernd Schoolmann <mail@quexten.com>
2025-10-20 14:47:15 -05:00

105 lines
3.1 KiB
TypeScript

import { systemPreferences } from "electron";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserId } from "@bitwarden/common/types/guid";
import { passwords } from "@bitwarden/desktop-napi";
import { BiometricsStatus } from "@bitwarden/key-management";
import { OsBiometricService } from "./os-biometrics.service";
const SERVICE = "Bitwarden_biometric";
function getLookupKeyForUser(userId: UserId): string {
return `${userId}_user_biometric`;
}
export default class OsBiometricsServiceMac implements OsBiometricService {
constructor(
private i18nservice: I18nService,
private logService: LogService,
) {}
async enrollPersistent(userId: UserId, key: SymmetricCryptoKey): Promise<void> {
return await passwords.setPassword(SERVICE, getLookupKeyForUser(userId), key.toBase64());
}
async hasPersistentKey(userId: UserId): Promise<boolean> {
return (await passwords.getPassword(SERVICE, getLookupKeyForUser(userId))) != null;
}
async supportsBiometrics(): Promise<boolean> {
return systemPreferences.canPromptTouchID();
}
async authenticateBiometric(): Promise<boolean> {
try {
await systemPreferences.promptTouchID(this.i18nservice.t("touchIdConsentMessage"));
return true;
} catch {
return false;
}
}
async getBiometricKey(userId: UserId): Promise<SymmetricCryptoKey | null> {
const success = await this.authenticateBiometric();
if (!success) {
throw new Error("Biometric authentication failed");
}
const keyB64 = await passwords.getPassword(SERVICE, getLookupKeyForUser(userId));
if (keyB64 == null) {
return null;
}
return SymmetricCryptoKey.fromString(keyB64);
}
async setBiometricKey(userId: UserId, key: SymmetricCryptoKey): Promise<void> {
if (await this.valueUpToDate(userId, key)) {
return;
}
return await passwords.setPassword(SERVICE, getLookupKeyForUser(userId), key.toBase64());
}
async deleteBiometricKey(user: UserId): Promise<void> {
try {
return await passwords.deletePassword(SERVICE, getLookupKeyForUser(user));
} catch (e) {
if (e instanceof Error && e.message === passwords.PASSWORD_NOT_FOUND) {
this.logService.debug(
"[OsBiometricService] Biometric key %s not found for service %s.",
getLookupKeyForUser(user),
SERVICE,
);
} else {
throw e;
}
}
}
private async valueUpToDate(user: UserId, key: SymmetricCryptoKey): Promise<boolean> {
try {
const existing = await passwords.getPassword(SERVICE, getLookupKeyForUser(user));
return existing === key.toBase64();
} catch {
return false;
}
}
async needsSetup() {
return false;
}
async canAutoSetup(): Promise<boolean> {
return false;
}
async runSetup(): Promise<void> {}
async getBiometricsFirstUnlockStatusForUser(userId: UserId): Promise<BiometricsStatus> {
return BiometricsStatus.Available;
}
}