mirror of
https://github.com/bitwarden/browser
synced 2026-01-04 17:43:39 +00:00
Rework Desktop Biometrics (#5234)
This commit is contained in:
@@ -108,9 +108,12 @@
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block" *ngIf="this.form.value.biometric">{{
|
||||
additionalBiometricSettingsText | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric && this.form.value.biometric">
|
||||
<div class="checkbox">
|
||||
<div class="checkbox form-group-child">
|
||||
<label for="autoPromptBiometrics">
|
||||
<input
|
||||
id="autoPromptBiometrics"
|
||||
@@ -122,6 +125,22 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric && this.form.value.biometric">
|
||||
<div class="checkbox form-group-child">
|
||||
<label for="requirePasswordOnStart">
|
||||
<input
|
||||
id="requirePasswordOnStart"
|
||||
type="checkbox"
|
||||
formControlName="requirePasswordOnStart"
|
||||
(change)="updateRequirePasswordOnStart()"
|
||||
/>
|
||||
{{ "requirePasswordOnStart" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block form-group-child" *ngIf="isWindows">{{
|
||||
"recommendedForSecurity" | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label for="approveLoginRequests">
|
||||
|
||||
@@ -9,15 +9,15 @@ import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { DeviceType, ThemeType, StorageLocation } from "@bitwarden/common/enums";
|
||||
import { DeviceType, ThemeType, StorageLocation, KeySuffixOptions } from "@bitwarden/common/enums";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
|
||||
import { flagEnabled } from "../../flags";
|
||||
import { ElectronStateService } from "../../services/electron-state.service.abstraction";
|
||||
import { isWindowsStore } from "../../utils";
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
|
||||
@@ -37,10 +37,12 @@ export class SettingsComponent implements OnInit {
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
biometricText: string;
|
||||
additionalBiometricSettingsText: string;
|
||||
autoPromptBiometricsText: string;
|
||||
showAlwaysShowDock = false;
|
||||
requireEnableTray = false;
|
||||
showDuckDuckGoIntegrationOption = false;
|
||||
isWindows: boolean;
|
||||
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
@@ -70,6 +72,7 @@ export class SettingsComponent implements OnInit {
|
||||
pin: [null as boolean | null],
|
||||
biometric: false,
|
||||
autoPromptBiometrics: false,
|
||||
requirePasswordOnStart: false,
|
||||
approveLoginRequests: false,
|
||||
// Account Preferences
|
||||
clearClipboard: [null as number | null],
|
||||
@@ -100,7 +103,7 @@ export class SettingsComponent implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private stateService: StateService,
|
||||
private stateService: ElectronStateService,
|
||||
private messagingService: MessagingService,
|
||||
private cryptoService: CryptoService,
|
||||
private modalService: ModalService,
|
||||
@@ -182,6 +185,8 @@ export class SettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.isWindows = (await this.platformUtilsService.getDevice()) === DeviceType.WindowsDesktop;
|
||||
|
||||
if ((await this.stateService.getUserId()) == null) {
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +221,9 @@ export class SettingsComponent implements OnInit {
|
||||
vaultTimeoutAction: await this.vaultTimeoutSettingsService.getVaultTimeoutAction(),
|
||||
pin: pinSet[0] || pinSet[1],
|
||||
biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(),
|
||||
autoPromptBiometrics: !(await this.stateService.getNoAutoPromptBiometrics()),
|
||||
autoPromptBiometrics: !(await this.stateService.getDisableAutoBiometricsPrompt()),
|
||||
requirePasswordOnStart:
|
||||
(await this.stateService.getBiometricRequirePasswordOnStart()) ?? false,
|
||||
approveLoginRequests: (await this.stateService.getApproveLoginRequests()) ?? false,
|
||||
clearClipboard: await this.stateService.getClearClipboard(),
|
||||
minimizeOnCopyToClipboard: await this.stateService.getMinimizeOnCopyToClipboard(),
|
||||
@@ -246,6 +253,10 @@ export class SettingsComponent implements OnInit {
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometricText = await this.stateService.getBiometricText();
|
||||
this.additionalBiometricSettingsText =
|
||||
this.biometricText === "unlockWithTouchId"
|
||||
? "additionalTouchIdSettings"
|
||||
: "additionalWindowsHelloSettings";
|
||||
this.autoPromptBiometricsText = await this.stateService.getNoAutoPromptBiometricsText();
|
||||
this.previousVaultTimeout = this.form.value.vaultTimeout;
|
||||
|
||||
@@ -379,26 +390,52 @@ export class SettingsComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
const authResult = await this.platformUtilsService.authenticateBiometric();
|
||||
|
||||
if (!authResult) {
|
||||
this.form.controls.biometric.setValue(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.form.controls.biometric.setValue(true);
|
||||
await this.stateService.setBiometricUnlock(true);
|
||||
if (this.isWindows) {
|
||||
// Recommended settings for Windows Hello
|
||||
this.form.controls.requirePasswordOnStart.setValue(true);
|
||||
this.form.controls.autoPromptBiometrics.setValue(false);
|
||||
await this.stateService.setDisableAutoBiometricsPrompt(true);
|
||||
await this.stateService.setBiometricRequirePasswordOnStart(true);
|
||||
await this.stateService.setDismissedBiometricRequirePasswordOnStart();
|
||||
}
|
||||
await this.cryptoService.toggleKey();
|
||||
|
||||
// Validate the key is stored in case biometrics fail.
|
||||
const biometricSet = await this.cryptoService.hasKeyStored(KeySuffixOptions.Biometric);
|
||||
this.form.controls.biometric.setValue(biometricSet);
|
||||
if (!biometricSet) {
|
||||
await this.stateService.setBiometricUnlock(null);
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutoPromptBiometrics() {
|
||||
if (this.form.value.autoPromptBiometrics) {
|
||||
await this.stateService.setNoAutoPromptBiometrics(null);
|
||||
// require password on start must be disabled if auto prompt biometrics is enabled
|
||||
this.form.controls.requirePasswordOnStart.setValue(false);
|
||||
await this.updateRequirePasswordOnStart();
|
||||
|
||||
await this.stateService.setDisableAutoBiometricsPrompt(null);
|
||||
} else {
|
||||
await this.stateService.setNoAutoPromptBiometrics(true);
|
||||
await this.stateService.setDisableAutoBiometricsPrompt(true);
|
||||
}
|
||||
}
|
||||
|
||||
async updateRequirePasswordOnStart() {
|
||||
if (this.form.value.requirePasswordOnStart) {
|
||||
// auto prompt biometrics must be disabled if require password on start is enabled
|
||||
this.form.controls.autoPromptBiometrics.setValue(false);
|
||||
await this.updateAutoPromptBiometrics();
|
||||
|
||||
await this.stateService.setBiometricRequirePasswordOnStart(true);
|
||||
} else {
|
||||
await this.stateService.setBiometricRequirePasswordOnStart(false);
|
||||
await this.stateService.setBiometricEncryptionClientKeyHalf(null);
|
||||
}
|
||||
await this.stateService.setDismissedBiometricRequirePasswordOnStart();
|
||||
await this.cryptoService.toggleKey();
|
||||
}
|
||||
|
||||
async saveFavicons() {
|
||||
await this.stateService.setDisableFavicon(!this.form.value.enableFavicons);
|
||||
await this.stateService.setDisableFavicon(!this.form.value.enableFavicons, {
|
||||
|
||||
@@ -48,11 +48,12 @@ import { ElectronPlatformUtilsService } from "../../services/electron-platform-u
|
||||
import { ElectronRendererMessagingService } from "../../services/electron-renderer-messaging.service";
|
||||
import { ElectronRendererSecureStorageService } from "../../services/electron-renderer-secure-storage.service";
|
||||
import { ElectronRendererStorageService } from "../../services/electron-renderer-storage.service";
|
||||
import { ElectronStateService } from "../../services/electron-state.service";
|
||||
import { ElectronStateService as ElectronStateServiceAbstraction } from "../../services/electron-state.service.abstraction";
|
||||
import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service";
|
||||
import { I18nService } from "../../services/i18n.service";
|
||||
import { NativeMessageHandlerService } from "../../services/native-message-handler.service";
|
||||
import { NativeMessagingService } from "../../services/native-messaging.service";
|
||||
import { StateService } from "../../services/state.service";
|
||||
import { PasswordRepromptService } from "../../vault/services/password-reprompt.service";
|
||||
import { SearchBarService } from "../layout/search/search-bar.service";
|
||||
|
||||
@@ -112,17 +113,6 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
{ provide: AbstractStorageService, useClass: ElectronRendererStorageService },
|
||||
{ provide: SECURE_STORAGE, useClass: ElectronRendererSecureStorageService },
|
||||
{ provide: MEMORY_STORAGE, useClass: MemoryStorageService },
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
EncryptService,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogServiceAbstraction,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: SystemServiceAbstraction,
|
||||
useClass: SystemService,
|
||||
@@ -136,7 +126,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
{ provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService },
|
||||
{
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
useClass: ElectronStateService,
|
||||
deps: [
|
||||
AbstractStorageService,
|
||||
SECURE_STORAGE,
|
||||
@@ -147,6 +137,10 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: ElectronStateServiceAbstraction,
|
||||
useExisting: StateServiceAbstraction,
|
||||
},
|
||||
{
|
||||
provide: FileDownloadService,
|
||||
useClass: DesktopFileDownloadService,
|
||||
@@ -182,6 +176,17 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
useClass: LoginService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
EncryptService,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ServicesModule {}
|
||||
|
||||
Reference in New Issue
Block a user