1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 10:43:35 +00:00

[PM-21705] Require userID for refreshAdditionalKeys() on key-service (#14810)

* Require userID for refreshAdditionalKeys()

* Add error handling to desktop Unlock settings

* Add more unit test coverage
This commit is contained in:
Thomas Avery
2025-06-06 13:38:25 -05:00
committed by GitHub
parent 3e4c37b8b3
commit 9d743a7ee0
8 changed files with 553 additions and 76 deletions

View File

@@ -37,6 +37,7 @@ 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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
@@ -162,6 +163,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
private logService: LogService,
private nativeMessagingManifestService: NativeMessagingManifestService,
private configService: ConfigService,
private validationService: ValidationService,
) {
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
@@ -379,7 +381,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
this.form.controls.pin.valueChanges
.pipe(
concatMap(async (value) => {
await this.updatePin(value);
await this.updatePinHandler(value);
this.refreshTimeoutSettings$.next();
}),
takeUntil(this.destroy$),
@@ -389,7 +391,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
this.form.controls.biometric.valueChanges
.pipe(
concatMap(async (enabled) => {
await this.updateBiometric(enabled);
await this.updateBiometricHandler(enabled);
this.refreshTimeoutSettings$.next();
}),
takeUntil(this.destroy$),
@@ -485,6 +487,18 @@ export class SettingsComponent implements OnInit, OnDestroy {
);
}
async updatePinHandler(value: boolean) {
try {
await this.updatePin(value);
} catch (error) {
this.logService.error("Error updating unlock with PIN: ", error);
this.form.controls.pin.setValue(!value, { emitEvent: false });
this.validationService.showError(error);
} finally {
this.messagingService.send("redrawMenu");
}
}
async updatePin(value: boolean) {
if (value) {
const dialogRef = SetPinComponent.open(this.dialogService);
@@ -509,8 +523,18 @@ export class SettingsComponent implements OnInit, OnDestroy {
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
await this.vaultTimeoutSettingsService.clear(userId);
}
}
this.messagingService.send("redrawMenu");
async updateBiometricHandler(value: boolean) {
try {
await this.updateBiometric(value);
} catch (error) {
this.logService.error("Error updating unlock with biometrics: ", error);
this.form.controls.biometric.setValue(false, { emitEvent: false });
this.validationService.showError(error);
} finally {
this.messagingService.send("redrawMenu");
}
}
async updateBiometric(enabled: boolean) {
@@ -519,61 +543,55 @@ export class SettingsComponent implements OnInit, OnDestroy {
// The bug should resolve itself once the angular issue is resolved.
// See: https://github.com/angular/angular/issues/13063
try {
if (!enabled || !this.supportsBiometric) {
this.form.controls.biometric.setValue(false, { emitEvent: false });
await this.biometricStateService.setBiometricUnlockEnabled(false);
await this.keyService.refreshAdditionalKeys();
return;
}
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (!enabled || !this.supportsBiometric) {
this.form.controls.biometric.setValue(false, { emitEvent: false });
await this.biometricStateService.setBiometricUnlockEnabled(false);
await this.keyService.refreshAdditionalKeys(activeUserId);
return;
}
const status = await this.biometricsService.getBiometricsStatus();
const status = await this.biometricsService.getBiometricsStatus();
if (status === BiometricsStatus.AutoSetupNeeded) {
await this.biometricsService.setupBiometrics();
} else if (status === BiometricsStatus.ManualSetupNeeded) {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "biometricsManualSetupTitle" },
content: { key: "biometricsManualSetupDesc" },
type: "warning",
});
if (confirmed) {
this.platformUtilsService.launchUri("https://bitwarden.com/help/biometrics/");
}
return;
if (status === BiometricsStatus.AutoSetupNeeded) {
await this.biometricsService.setupBiometrics();
} else if (status === BiometricsStatus.ManualSetupNeeded) {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "biometricsManualSetupTitle" },
content: { key: "biometricsManualSetupDesc" },
type: "warning",
});
if (confirmed) {
this.platformUtilsService.launchUri("https://bitwarden.com/help/biometrics/");
}
return;
}
await this.biometricStateService.setBiometricUnlockEnabled(true);
if (this.isWindows) {
// Recommended settings for Windows Hello
this.form.controls.requirePasswordOnStart.setValue(true);
this.form.controls.autoPromptBiometrics.setValue(false);
await this.biometricStateService.setPromptAutomatically(false);
await this.biometricStateService.setRequirePasswordOnStart(true);
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
} else if (this.isLinux) {
// Similar to Windows
this.form.controls.requirePasswordOnStart.setValue(true);
this.form.controls.autoPromptBiometrics.setValue(false);
await this.biometricStateService.setPromptAutomatically(false);
await this.biometricStateService.setRequirePasswordOnStart(true);
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
}
await this.keyService.refreshAdditionalKeys();
await this.biometricStateService.setBiometricUnlockEnabled(true);
if (this.isWindows) {
// Recommended settings for Windows Hello
this.form.controls.requirePasswordOnStart.setValue(true);
this.form.controls.autoPromptBiometrics.setValue(false);
await this.biometricStateService.setPromptAutomatically(false);
await this.biometricStateService.setRequirePasswordOnStart(true);
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
} else if (this.isLinux) {
// Similar to Windows
this.form.controls.requirePasswordOnStart.setValue(true);
this.form.controls.autoPromptBiometrics.setValue(false);
await this.biometricStateService.setPromptAutomatically(false);
await this.biometricStateService.setRequirePasswordOnStart(true);
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
}
await this.keyService.refreshAdditionalKeys(activeUserId);
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
// Validate the key is stored in case biometrics fail.
const biometricSet =
(await this.biometricsService.getBiometricsStatusForUser(activeUserId)) ===
BiometricsStatus.Available;
this.form.controls.biometric.setValue(biometricSet, { emitEvent: false });
if (!biometricSet) {
await this.biometricStateService.setBiometricUnlockEnabled(false);
}
} finally {
this.messagingService.send("redrawMenu");
// Validate the key is stored in case biometrics fail.
const biometricSet =
(await this.biometricsService.getBiometricsStatusForUser(activeUserId)) ===
BiometricsStatus.Available;
this.form.controls.biometric.setValue(biometricSet, { emitEvent: false });
if (!biometricSet) {
await this.biometricStateService.setBiometricUnlockEnabled(false);
}
}
@@ -599,7 +617,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
await this.biometricStateService.setRequirePasswordOnStart(false);
}
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
await this.keyService.refreshAdditionalKeys();
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
await this.keyService.refreshAdditionalKeys(userId);
}
async saveFavicons() {