Ensure biometric unlock works even if popup is not in focus
This commit is contained in:
parent
5eb0ce1e09
commit
894d245361
|
@ -1251,7 +1251,10 @@
|
|||
"message": "Unlock with biometric"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "Awaiting biometric confirmation from desktop application."
|
||||
"message": "Awaiting confirmation from desktop"
|
||||
},
|
||||
"awaitDesktopDesc": {
|
||||
"message": "Please confirm using biometrics in the Bitwarden desktop application to enable biometrics for browser."
|
||||
},
|
||||
"lockWithMasterPassOnRestart": {
|
||||
"message": "Lock with master password on browser restart"
|
||||
|
|
|
@ -141,8 +141,6 @@ export default class MainBackground {
|
|||
private nativeMessagingBackground: NativeMessagingBackground;
|
||||
|
||||
constructor() {
|
||||
this.nativeMessagingBackground = new NativeMessagingBackground();
|
||||
|
||||
// Services
|
||||
this.messagingService = new BrowserMessagingService();
|
||||
this.platformUtilsService = new BrowserPlatformUtilsService(this.messagingService,
|
||||
|
@ -150,7 +148,14 @@ export default class MainBackground {
|
|||
if (this.systemService != null) {
|
||||
this.systemService.clearClipboard(clipboardValue, clearMs);
|
||||
}
|
||||
}, this.nativeMessagingBackground);
|
||||
},
|
||||
() => {
|
||||
if (this.nativeMessagingBackground != null) {
|
||||
const promise = this.nativeMessagingBackground.await();
|
||||
this.nativeMessagingBackground.send({command: 'biometricUnlock'})
|
||||
return promise.then((result) => result.response === 'unlocked');
|
||||
}
|
||||
});
|
||||
this.storageService = new BrowserStorageService(this.platformUtilsService);
|
||||
this.secureStorageService = new BrowserStorageService(this.platformUtilsService);
|
||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(window));
|
||||
|
@ -228,6 +233,7 @@ export default class MainBackground {
|
|||
this.platformUtilsService as BrowserPlatformUtilsService, this.storageService, this.i18nService,
|
||||
this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService,
|
||||
this.environmentService);
|
||||
this.nativeMessagingBackground = new NativeMessagingBackground(this.storageService, this.cryptoService, this.vaultTimeoutService, this.runtimeBackground);
|
||||
this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService,
|
||||
this.platformUtilsService, this.analytics, this.vaultTimeoutService);
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { CryptoService, VaultTimeoutService } from 'jslib/abstractions';
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
import { ConstantsService } from 'jslib/services';
|
||||
import { BrowserApi } from '../browser/browserApi';
|
||||
import RuntimeBackground from './runtime.background';
|
||||
|
||||
export class NativeMessagingBackground {
|
||||
private connected = false;
|
||||
|
@ -6,18 +10,16 @@ export class NativeMessagingBackground {
|
|||
|
||||
private resolver: any = null;
|
||||
|
||||
constructor(private storageService: StorageService, private cryptoService: CryptoService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private runtimeBackground: RuntimeBackground) {}
|
||||
|
||||
connect() {
|
||||
this.port = BrowserApi.connectNative('com.8bit.bitwarden');
|
||||
|
||||
this.connected = true;
|
||||
this.port.onMessage.addListener((msg: any) => {
|
||||
if (this.resolver) {
|
||||
this.resolver(msg);
|
||||
} else {
|
||||
// tslint:disable-next-line
|
||||
console.error('NO RESOLVER');
|
||||
}
|
||||
});
|
||||
|
||||
this.port.onMessage.addListener((msg) => this.onMessage(msg));
|
||||
|
||||
this.port.onDisconnect.addListener(() => {
|
||||
this.connected = false;
|
||||
});
|
||||
|
@ -37,4 +39,30 @@ export class NativeMessagingBackground {
|
|||
this.resolver = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
private async onMessage(msg: any) {
|
||||
switch(msg.command) {
|
||||
case 'biometricUnlock': {
|
||||
await this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
||||
|
||||
const enabled = await this.storageService.get(ConstantsService.biometricUnlockKey);
|
||||
if (enabled === null || enabled === false) {
|
||||
if (msg.response === 'unlocked') {
|
||||
await this.storageService.save(ConstantsService.biometricUnlockKey, true);
|
||||
}
|
||||
|
||||
await this.cryptoService.toggleKey();
|
||||
}
|
||||
|
||||
if (this.vaultTimeoutService.biometricLocked) {
|
||||
this.runtimeBackground.processMessage({command: 'unlocked'}, null, null);
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.resolver) {
|
||||
this.resolver(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
|
|||
import { BrowserApi } from '../browser/browserApi';
|
||||
|
||||
import MainBackground from './main.background';
|
||||
import { NativeMessagingBackground } from './nativeMessaging.background';
|
||||
|
||||
import { Analytics } from 'jslib/misc';
|
||||
import { Utils } from 'jslib/misc/utils';
|
||||
|
|
|
@ -51,7 +51,7 @@ export class SettingsComponent implements OnInit {
|
|||
vaultTimeoutActions: any[];
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
biometric: boolean = null;
|
||||
biometric: boolean = false;
|
||||
previousVaultTimeout: number = null;
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||
|
@ -207,40 +207,45 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
async updateBiometric() {
|
||||
const current = this.biometric;
|
||||
if (this.biometric) {
|
||||
this.biometric = false;
|
||||
// TODO: Remove biometric stuff
|
||||
await this.storageService.remove(ConstantsService.biometricUnlockKey);
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
} else {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<div class="swal2-text">${this.i18nService.t('awaitDesktop')}</div>`;
|
||||
|
||||
const submitted = Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
title: this.i18nService.t('awaitDesktop'),
|
||||
text: this.i18nService.t('awaitDesktopDesc'),
|
||||
icon: 'info',
|
||||
iconHtml: '<i class="swal-custom-icon fa fa-info-circle text-info"></i>',
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('cancel'),
|
||||
showConfirmButton: false,
|
||||
allowOutsideClick: false,
|
||||
});
|
||||
|
||||
// TODO: Show waiting message
|
||||
this.biometric = await this.platformUtilsService.authenticateBiometric();
|
||||
Swal.close();
|
||||
await this.storageService.save(ConstantsService.biometricAwaitingAcceptance, true);
|
||||
await this.cryptoService.toggleKey();
|
||||
|
||||
if (this.biometric === false) {
|
||||
this.platformUtilsService.showToast('error', 'Unable to enable biometrics', 'Ensure the desktop application is running, and browser integration is enabled.');
|
||||
}
|
||||
await Promise.race([
|
||||
submitted.then((result) => {
|
||||
if (result.dismiss === Swal.DismissReason.cancel) {
|
||||
this.biometric = false;
|
||||
this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
||||
}
|
||||
}),
|
||||
this.platformUtilsService.authenticateBiometric().then((result) => {
|
||||
this.biometric = result;
|
||||
|
||||
Swal.close();
|
||||
if (this.biometric === false) {
|
||||
this.platformUtilsService.showToast('error', 'Unable to enable biometrics', 'Ensure the desktop application is running, and browser integration is enabled.');
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
if (this.biometric === current) {
|
||||
return;
|
||||
}
|
||||
if (this.biometric) {
|
||||
await this.storageService.save(ConstantsService.biometricUnlockKey, true);
|
||||
} else {
|
||||
await this.storageService.remove(ConstantsService.biometricUnlockKey);
|
||||
}
|
||||
this.vaultTimeoutService.biometricLocked = false;
|
||||
await this.cryptoService.toggleKey();
|
||||
}
|
||||
|
||||
async lock() {
|
||||
|
|
|
@ -20,7 +20,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
|||
|
||||
constructor(private messagingService: MessagingService,
|
||||
private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
|
||||
private nativeMessagingBackground: NativeMessagingBackground) { }
|
||||
private biometricCallback: () => Promise<boolean>) { }
|
||||
|
||||
getDevice(): DeviceType {
|
||||
if (this.deviceCache) {
|
||||
|
@ -293,13 +293,8 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
|||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
async authenticateBiometric() {
|
||||
const responsePromise = this.nativeMessagingBackground.await();
|
||||
this.nativeMessagingBackground.send({'command': 'biometricUnlock'});
|
||||
|
||||
const response = await responsePromise;
|
||||
|
||||
return response.response === 'unlocked';
|
||||
authenticateBiometric() {
|
||||
return this.biometricCallback();
|
||||
}
|
||||
|
||||
sidebarViewName(): string {
|
||||
|
|
Loading…
Reference in New Issue