mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-19215] Fix Firefox extension biometric unlock autoprompt (#14254)
* Remove restriction from account security component * Add the ability to manage pop-out to the LockComponentService * Have the Firefox extension pop-out on biometric auto-prompt unlock
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<ng-template #loading>
|
||||
<div class="tw-flex tw-items-center tw-justify-center" *ngIf="loading">
|
||||
<ng-template #spinner>
|
||||
<div class="tw-flex tw-items-center tw-justify-center">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-container *ngIf="unlockOptions; else loading">
|
||||
<ng-container *ngIf="unlockOptions && !loading; else spinner">
|
||||
<!-- Biometrics Unlock -->
|
||||
<ng-container *ngIf="activeUnlockOption === UnlockOption.Biometrics">
|
||||
<button
|
||||
|
||||
@@ -4,6 +4,7 @@ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from "@angula
|
||||
import { Router } from "@angular/router";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
filter,
|
||||
firstValueFrom,
|
||||
interval,
|
||||
mergeMap,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
switchMap,
|
||||
take,
|
||||
takeUntil,
|
||||
tap,
|
||||
} from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
@@ -88,6 +90,7 @@ const AUTOPROMPT_BIOMETRICS_PROCESS_RELOAD_DELAY = 5000;
|
||||
})
|
||||
export class LockComponent implements OnInit, OnDestroy {
|
||||
private destroy$ = new Subject<void>();
|
||||
protected loading = true;
|
||||
|
||||
activeAccount: Account | null = null;
|
||||
|
||||
@@ -122,6 +125,9 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
|
||||
formGroup: FormGroup | null = null;
|
||||
|
||||
// Browser extension properties:
|
||||
private shouldClosePopout = false;
|
||||
|
||||
// Desktop properties:
|
||||
private deferFocus: boolean | null = null;
|
||||
private biometricAsked = false;
|
||||
@@ -228,22 +234,22 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
private listenForActiveAccountChanges() {
|
||||
this.accountService.activeAccount$
|
||||
.pipe(
|
||||
switchMap((account) => {
|
||||
return this.handleActiveAccountChange(account);
|
||||
tap((account) => {
|
||||
this.loading = true;
|
||||
this.activeAccount = account;
|
||||
this.resetDataOnActiveAccountChange();
|
||||
}),
|
||||
filter((account): account is Account => account != null),
|
||||
switchMap(async (account) => {
|
||||
await this.handleActiveAccountChange(account);
|
||||
this.loading = false;
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private async handleActiveAccountChange(activeAccount: Account | null) {
|
||||
this.activeAccount = activeAccount;
|
||||
|
||||
this.resetDataOnActiveAccountChange();
|
||||
|
||||
if (activeAccount == null) {
|
||||
return;
|
||||
}
|
||||
private async handleActiveAccountChange(activeAccount: Account) {
|
||||
// this account may be unlocked, prevent any prompts so we can redirect to vault
|
||||
if (await this.keyService.hasUserKeyInMemory(activeAccount.id)) {
|
||||
return;
|
||||
@@ -300,16 +306,12 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
// desktop and extension.
|
||||
if (this.clientType === "desktop") {
|
||||
if (autoPromptBiometrics) {
|
||||
this.loading = false;
|
||||
await this.desktopAutoPromptBiometrics();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.clientType === "browser") {
|
||||
// Firefox closes the popup when unfocused, so this would block all unlock methods
|
||||
if (this.platformUtilsService.getDevice() === DeviceType.FirefoxExtension) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
this.unlockOptions?.biometrics.enabled &&
|
||||
autoPromptBiometrics &&
|
||||
@@ -323,6 +325,12 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
isNaN(lastProcessReload.getTime()) ||
|
||||
Date.now() - lastProcessReload.getTime() > AUTOPROMPT_BIOMETRICS_PROCESS_RELOAD_DELAY
|
||||
) {
|
||||
// Firefox extension closes the popup when unfocused during biometric unlock, pop out the window to prevent infinite loop.
|
||||
if (this.platformUtilsService.getDevice() === DeviceType.FirefoxExtension) {
|
||||
await this.lockComponentService.popOutBrowserExtension();
|
||||
this.shouldClosePopout = true;
|
||||
}
|
||||
this.loading = false;
|
||||
await this.unlockViaBiometrics();
|
||||
}
|
||||
}
|
||||
@@ -637,6 +645,13 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
const successRoute = clientTypeToSuccessRouteRecord[this.clientType];
|
||||
await this.router.navigate([successRoute]);
|
||||
}
|
||||
|
||||
if (
|
||||
this.shouldClosePopout &&
|
||||
this.platformUtilsService.getDevice() === DeviceType.FirefoxExtension
|
||||
) {
|
||||
this.lockComponentService.closeBrowserExtensionPopout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,18 @@ export abstract class LockComponentService {
|
||||
// Extension
|
||||
abstract getBiometricsError(error: any): string | null;
|
||||
abstract getPreviousUrl(): string | null;
|
||||
/**
|
||||
* Opens the current page in a popout window if not already in a popout or the sidebar.
|
||||
* If already in a popout or sidebar, does nothing.
|
||||
* @throws Error if execution context is not a browser extension.
|
||||
*/
|
||||
abstract popOutBrowserExtension(): Promise<void>;
|
||||
/**
|
||||
* Closes the current popout window if in a popout.
|
||||
* If not in a popout, does nothing.
|
||||
* @throws Error if execution context is not a browser extension.
|
||||
*/
|
||||
abstract closeBrowserExtensionPopout(): void;
|
||||
|
||||
// Desktop only
|
||||
abstract isWindowVisible(): Promise<boolean>;
|
||||
|
||||
Reference in New Issue
Block a user