diff --git a/apps/desktop/src/auth/lock.component.ts b/apps/desktop/src/auth/lock.component.ts index 98a2a0e28b..b52cd8d2c7 100644 --- a/apps/desktop/src/auth/lock.component.ts +++ b/apps/desktop/src/auth/lock.component.ts @@ -136,6 +136,10 @@ export class LockComponent extends BaseLockComponent { return; } + if (await this.stateService.getBiometricPromptCancelled()) { + return; + } + this.biometricAsked = true; if (await ipc.platform.isWindowVisible()) { this.unlockBiometric(); diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index c33018b9fb..511f38eba9 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -89,6 +89,8 @@ export class WindowMain { // This method will be called when Electron is shutting // down the application. app.on("before-quit", () => { + // Allow biometric to auto-prompt on reload + this.stateService.setBiometricPromptCancelled(false); this.isQuitting = true; }); diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts index 604fdff476..d03b28abdb 100644 --- a/libs/angular/src/auth/components/lock.component.ts +++ b/libs/angular/src/auth/components/lock.component.ts @@ -118,6 +118,7 @@ export class LockComponent implements OnInit, OnDestroy { return; } + await this.stateService.setBiometricPromptCancelled(true); const userKey = await this.cryptoService.getUserKeyFromStorage(KeySuffixOptions.Biometric); if (userKey) { @@ -274,6 +275,7 @@ export class LockComponent implements OnInit, OnDestroy { private async doContinue(evaluatePasswordAfterUnlock: boolean) { await this.stateService.setEverBeenUnlocked(true); + await this.stateService.setBiometricPromptCancelled(false); this.messagingService.send("unlocked"); if (evaluatePasswordAfterUnlock) { diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 46759147cc..2080e81024 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -177,6 +177,18 @@ export abstract class StateService { * @deprecated For migration purposes only, use setUserKeyBiometric instead */ setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise; + /** + * Gets a flag for if the biometrics process has been cancelled. + * Process reload occurs when biometrics is cancelled, so we store to disk to prevent + * it from reprompting and creating a loop. + */ + getBiometricPromptCancelled: (options?: StorageOptions) => Promise; + /** + * Sets a flag for if the biometrics process has been cancelled. + * Process reload occurs when biometrics is cancelled, so we store to disk to prevent + * it from reprompting and creating a loop. + */ + setBiometricPromptCancelled: (value: boolean, options?: StorageOptions) => Promise; getDecryptedCiphers: (options?: StorageOptions) => Promise; setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise; getDecryptedCollections: (options?: StorageOptions) => Promise; diff --git a/libs/common/src/platform/models/domain/account.ts b/libs/common/src/platform/models/domain/account.ts index ac192ab22d..50a0062e4e 100644 --- a/libs/common/src/platform/models/domain/account.ts +++ b/libs/common/src/platform/models/domain/account.ts @@ -267,6 +267,7 @@ export class AccountSettings { region?: string; smOnboardingTasks?: Record>; trustDeviceChoiceForDecryption?: boolean; + biometricPromptCancelled?: boolean; /** @deprecated July 2023, left for migration purposes*/ pinProtected?: EncryptionPair = new EncryptionPair(); diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 0e13ade372..7808e2ba31 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -910,6 +910,24 @@ export class StateService< await this.saveSecureStorageKey(partialKeys.biometricKey, value, options); } + async getBiometricPromptCancelled(options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()), + ); + return account?.settings?.biometricPromptCancelled; + } + + async setBiometricPromptCancelled(value: boolean, options?: StorageOptions): Promise { + const account = await this.getAccount( + this.reconcileOptions(options, await this.defaultOnDiskOptions()), + ); + account.settings.biometricPromptCancelled = value; + await this.saveAccount( + account, + this.reconcileOptions(options, await this.defaultOnDiskOptions()), + ); + } + @withPrototypeForArrayMembers(CipherView, CipherView.fromJSON) async getDecryptedCiphers(options?: StorageOptions): Promise { return (