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

[PM-17038] Fix biometrics autoprompt in firefox and chrome (#12853)

* Fix biometrics not working in firefox or windows

* Remove logs

* Update badge after biometric unlock

* Add removal todo note

* Remove debug logging

* Fix type warnings

* Fix userkey typing in background biometrics service

* Simplify types for userkey in foreground-browser-biometrics and runtime.background.ts

* Add process reload logging

* Fix autoprompt not working when no process reload happened

* Fix instant reprompt on firefox lock

* Fix biometrics enabling error on chrome

* Update apps/browser/src/key-management/biometrics/foreground-browser-biometrics.ts

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>

* FIx build & linting

---------

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>
This commit is contained in:
Bernd Schoolmann
2025-01-14 18:29:52 +01:00
committed by GitHub
parent 63a9c69f5a
commit 318a3ac6a9
4 changed files with 22 additions and 6 deletions

View File

@@ -1,3 +1,4 @@
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { UserKey } from "@bitwarden/common/types/key"; import { UserKey } from "@bitwarden/common/types/key";
import { BiometricsCommands, BiometricsService, BiometricsStatus } from "@bitwarden/key-management"; import { BiometricsCommands, BiometricsService, BiometricsStatus } from "@bitwarden/key-management";
@@ -34,7 +35,7 @@ export class ForegroundBrowserBiometricsService extends BiometricsService {
if (!response.result) { if (!response.result) {
return null; return null;
} }
return response.result; return SymmetricCryptoKey.fromString(response.result.keyB64) as UserKey;
} }
async getBiometricsStatusForUser(id: UserId): Promise<BiometricsStatus> { async getBiometricsStatusForUser(id: UserId): Promise<BiometricsStatus> {

View File

@@ -21,7 +21,7 @@ import {
ToastOptions, ToastOptions,
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { BiometricStateService } from "@bitwarden/key-management"; import { BiometricsService, BiometricStateService } from "@bitwarden/key-management";
import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service"; import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service";
import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service";
@@ -66,6 +66,7 @@ export class AppComponent implements OnInit, OnDestroy {
private accountService: AccountService, private accountService: AccountService,
private animationControlService: AnimationControlService, private animationControlService: AnimationControlService,
private biometricStateService: BiometricStateService, private biometricStateService: BiometricStateService,
private biometricsService: BiometricsService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
@@ -102,7 +103,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.messageListener.allMessages$ this.messageListener.allMessages$
.pipe( .pipe(
tap((msg: any) => { tap(async (msg: any) => {
if (msg.command === "doneLoggingOut") { if (msg.command === "doneLoggingOut") {
// TODO: PM-8544 - why do we call logout in the popup after receiving the doneLoggingOut message? Hasn't this already completeted logout? // TODO: PM-8544 - why do we call logout in the popup after receiving the doneLoggingOut message? Hasn't this already completeted logout?
this.authService.logOut(async () => { this.authService.logOut(async () => {
@@ -119,6 +120,7 @@ export class AppComponent implements OnInit, OnDestroy {
msg.command === "locked" && msg.command === "locked" &&
(msg.userId == null || msg.userId == this.activeUserId) (msg.userId == null || msg.userId == this.activeUserId)
) { ) {
await this.biometricsService.setShouldAutopromptNow(false);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["lock"]); this.router.navigate(["lock"]);

View File

@@ -39,6 +39,9 @@ export class DefaultProcessReloadService implements ProcessReloadServiceAbstract
let status = await firstValueFrom(authService.authStatusFor$(userId as UserId)); let status = await firstValueFrom(authService.authStatusFor$(userId as UserId));
status = await authService.getAuthStatus(userId); status = await authService.getAuthStatus(userId);
if (status === AuthenticationStatus.Unlocked) { if (status === AuthenticationStatus.Unlocked) {
this.logService.info(
"[Process Reload Service] User unlocked, preventing process reload",
);
return; return;
} }
} }
@@ -55,6 +58,9 @@ export class DefaultProcessReloadService implements ProcessReloadServiceAbstract
if (userId != null) { if (userId != null) {
const ephemeralPin = await this.pinService.getPinKeyEncryptedUserKeyEphemeral(userId); const ephemeralPin = await this.pinService.getPinKeyEncryptedUserKeyEphemeral(userId);
if (ephemeralPin != null) { if (ephemeralPin != null) {
this.logService.info(
"[Process Reload Service] Ephemeral pin active, preventing process reload",
);
return; return;
} }
} }
@@ -97,7 +103,12 @@ export class DefaultProcessReloadService implements ProcessReloadServiceAbstract
await this.reloadCallback(); await this.reloadCallback();
} }
return; return;
} else {
this.logService.info(
"[Process Reload Service] Desktop ipc fingerprint validated, preventing process reload",
);
} }
if (this.reloadInterval == null) { if (this.reloadInterval == null) {
this.reloadInterval = setInterval(async () => await this.executeProcessReload(), 1000); this.reloadInterval = setInterval(async () => await this.executeProcessReload(), 1000);
} }

View File

@@ -307,10 +307,12 @@ export class LockComponent implements OnInit, OnDestroy {
(await this.biometricService.getShouldAutopromptNow()) (await this.biometricService.getShouldAutopromptNow())
) { ) {
await this.biometricService.setShouldAutopromptNow(false); await this.biometricService.setShouldAutopromptNow(false);
const lastProcessReload = await this.biometricStateService.getLastProcessReload();
if ( if (
(await this.biometricStateService.getLastProcessReload()) == null || lastProcessReload == null ||
Date.now() - (await this.biometricStateService.getLastProcessReload()).getTime() > isNaN(lastProcessReload.getTime()) ||
AUTOPROMPT_BIOMETRICS_PROCESS_RELOAD_DELAY Date.now() - lastProcessReload.getTime() > AUTOPROMPT_BIOMETRICS_PROCESS_RELOAD_DELAY
) { ) {
await this.unlockViaBiometrics(); await this.unlockViaBiometrics();
} }