mirror of
https://github.com/bitwarden/browser
synced 2026-02-05 11:13:44 +00:00
Re-add biometric unlock on app start to Windows Hello
This commit is contained in:
@@ -80,6 +80,19 @@
|
||||
<small class="help-block" *ngIf="this.form.value.biometric && this.isMac">{{
|
||||
"additionalTouchIdSettings" | i18n
|
||||
}}</small>
|
||||
<!-- Textbox for biometric app start -->
|
||||
<div class="form-group tw-mt-2" *ngIf="this.form.value.biometric && this.isWindows">
|
||||
<div class="checkbox">
|
||||
<label for="allowBiometricUnlockOnAppRestart">
|
||||
<input
|
||||
id="allowBiometricUnlockOnAppRestart"
|
||||
type="checkbox"
|
||||
formControlName="allowBiometricUnlockOnAppRestart"
|
||||
/>
|
||||
{{ "allowBiometricUnlockOnAppRestart" | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="form-group"
|
||||
|
||||
@@ -134,6 +134,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
vaultTimeoutAction: [VaultTimeoutAction.Lock],
|
||||
pin: [null as boolean | null],
|
||||
biometric: false,
|
||||
allowBiometricUnlockOnAppRestart: false,
|
||||
autoPromptBiometrics: false,
|
||||
// Account Preferences
|
||||
clearClipboard: [null],
|
||||
@@ -348,6 +349,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
),
|
||||
pin: this.userHasPinSet,
|
||||
biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(),
|
||||
allowBiometricUnlockOnAppRestart: await this.biometricsService.hasPersistentKey(
|
||||
activeAccount.id,
|
||||
),
|
||||
autoPromptBiometrics: await firstValueFrom(this.biometricStateService.promptAutomatically$),
|
||||
clearClipboard: await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$),
|
||||
minimizeOnCopyToClipboard: await firstValueFrom(this.desktopSettingsService.minimizeOnCopy$),
|
||||
@@ -440,6 +444,25 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
this.form.controls.allowBiometricUnlockOnAppRestart.valueChanges
|
||||
.pipe(
|
||||
concatMap(async (enabled) => {
|
||||
const userKey = await firstValueFrom(this.keyService.userKey$(activeAccount.id));
|
||||
if (enabled) {
|
||||
if (!(await this.biometricsService.hasPersistentKey(activeAccount.id))) {
|
||||
await this.biometricsService.enrollPersistent(activeAccount.id, userKey);
|
||||
}
|
||||
} else {
|
||||
await this.biometricsService.deleteBiometricUnlockKeyForUser(activeAccount.id);
|
||||
await this.biometricsService.setBiometricProtectedUnlockKeyForUser(
|
||||
activeAccount.id,
|
||||
userKey,
|
||||
);
|
||||
}
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.form.controls.enableBrowserIntegration.valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
|
||||
@@ -13,4 +13,6 @@ export abstract class DesktopBiometricsService extends BiometricsService {
|
||||
): Promise<void>;
|
||||
abstract deleteBiometricUnlockKeyForUser(userId: UserId): Promise<void>;
|
||||
abstract setupBiometrics(): Promise<void>;
|
||||
abstract enrollPersistent(userId: UserId, key: SymmetricCryptoKey): Promise<void>;
|
||||
abstract hasPersistentKey(userId: UserId): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ export class MainBiometricsIPCListener {
|
||||
return await this.biometricService.setShouldAutopromptNow(message.data as boolean);
|
||||
case BiometricAction.GetShouldAutoprompt:
|
||||
return await this.biometricService.getShouldAutopromptNow();
|
||||
case BiometricAction.HasPersistentKey:
|
||||
return await this.biometricService.hasPersistentKey(message.userId as UserId);
|
||||
case BiometricAction.EnrollPersistent:
|
||||
return await this.biometricService.enrollPersistent(
|
||||
message.userId as UserId,
|
||||
SymmetricCryptoKey.fromString(message.key as string),
|
||||
);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -128,4 +128,12 @@ export class MainBiometricsService extends DesktopBiometricsService {
|
||||
async canEnableBiometricUnlock(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
async enrollPersistent(userId: UserId, key: SymmetricCryptoKey): Promise<void> {
|
||||
return await this.osBiometricsService.enrollPersistent(userId, key);
|
||||
}
|
||||
|
||||
async hasPersistentKey(userId: UserId): Promise<boolean> {
|
||||
return await this.osBiometricsService.hasPersistentKey(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,4 +68,12 @@ export class RendererBiometricsService extends DesktopBiometricsService {
|
||||
BiometricsStatus.ManualSetupNeeded,
|
||||
].includes(biometricStatus);
|
||||
}
|
||||
|
||||
async enrollPersistent(userId: UserId, key: SymmetricCryptoKey): Promise<void> {
|
||||
return await ipc.keyManagement.biometric.enrollPersistent(userId, key.toBase64());
|
||||
}
|
||||
|
||||
async hasPersistentKey(userId: UserId): Promise<boolean> {
|
||||
return await ipc.keyManagement.biometric.hasPersistentKey(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,17 @@ const biometric = {
|
||||
action: BiometricAction.SetShouldAutoprompt,
|
||||
data: should,
|
||||
} satisfies BiometricMessage),
|
||||
enrollPersistent: (userId: string, keyB64: string): Promise<void> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.EnrollPersistent,
|
||||
userId: userId,
|
||||
key: keyB64,
|
||||
} satisfies BiometricMessage),
|
||||
hasPersistentKey: (userId: string): Promise<boolean> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.HasPersistentKey,
|
||||
userId: userId,
|
||||
} satisfies BiometricMessage),
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1849,6 +1849,9 @@
|
||||
"lockWithMasterPassOnRestart1": {
|
||||
"message": "Lock with master password on restart"
|
||||
},
|
||||
"allowBiometricUnlockOnAppRestart": {
|
||||
"message": "Allow biometric unlock on app restart"
|
||||
},
|
||||
"deleteAccount": {
|
||||
"message": "Delete account"
|
||||
},
|
||||
|
||||
@@ -13,6 +13,9 @@ export enum BiometricAction {
|
||||
|
||||
GetShouldAutoprompt = "getShouldAutoprompt",
|
||||
SetShouldAutoprompt = "setShouldAutoprompt",
|
||||
|
||||
EnrollPersistent = "enrollPersistent",
|
||||
HasPersistentKey = "hasPersistentKey",
|
||||
}
|
||||
|
||||
export type BiometricMessage =
|
||||
@@ -22,7 +25,15 @@ export type BiometricMessage =
|
||||
key: string;
|
||||
}
|
||||
| {
|
||||
action: Exclude<BiometricAction, BiometricAction.SetKeyForUser>;
|
||||
action: BiometricAction.EnrollPersistent;
|
||||
userId: string;
|
||||
key: string;
|
||||
}
|
||||
| {
|
||||
action: Exclude<
|
||||
BiometricAction,
|
||||
BiometricAction.SetKeyForUser | BiometricAction.EnrollPersistent
|
||||
>;
|
||||
userId?: string;
|
||||
data?: any;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user