Add support for browser biometrics on Firefox >= 87 (#1668)

This commit is contained in:
Oscar Hinton 2021-03-10 21:27:05 +01:00 committed by Chad Scharf
parent d0b307d36a
commit faf4c3e94a
5 changed files with 39 additions and 29 deletions

View File

@ -58,7 +58,6 @@ function dist(browserName, manifest) {
function distFirefox() { function distFirefox() {
return dist('firefox', (manifest) => { return dist('firefox', (manifest) => {
delete manifest.content_security_policy; delete manifest.content_security_policy;
delete manifest.optional_permissions;
removeShortcuts(manifest); removeShortcuts(manifest);
return manifest; return manifest;
}); });

View File

@ -1441,18 +1441,18 @@
"biometricsNotSupportedDesc": { "biometricsNotSupportedDesc": {
"message": "Browser biometrics is not supported on this device." "message": "Browser biometrics is not supported on this device."
}, },
"nativeMessagingPermissionPromptTitle": {
"message": "Additional Permission required"
},
"nativeMessagingPermissionPromptDesc": {
"message": "To enable browser biometrics we need to request an additional permission. Once allowed, the browser extension will reload and you may need to unlock your vault again."
},
"nativeMessaginPermissionErrorTitle": { "nativeMessaginPermissionErrorTitle": {
"message": "Permission not provided" "message": "Permission not provided"
}, },
"nativeMessaginPermissionErrorDesc": { "nativeMessaginPermissionErrorDesc": {
"message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again."
}, },
"nativeMessaginPermissionSidebarTitle": {
"message": "Permission request error"
},
"nativeMessaginPermissionSidebarDesc": {
"message": "This action cannot be done in the sidebar, please retry the action in the popup or popout."
},
"personalOwnershipSubmitError": { "personalOwnershipSubmitError": {
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections." "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
}, },

View File

@ -167,4 +167,13 @@ export class BrowserApi {
return chrome.runtime.connectNative(application); return chrome.runtime.connectNative(application);
} }
} }
static requestPermission(permission: any) {
if (BrowserApi.isWebExtensionsApi) {
return browser.permissions.request(permission);
}
return new Promise((resolve, reject) => {
chrome.permissions.request(permission, resolve);
});
}
} }

View File

@ -23,6 +23,7 @@ import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StorageService } from 'jslib/abstractions/storage.service'; import { StorageService } from 'jslib/abstractions/storage.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service'; import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { PopupUtilsService } from '../services/popup-utils.service';
const RateUrls = { const RateUrls = {
[DeviceType.ChromeExtension]: [DeviceType.ChromeExtension]:
@ -59,7 +60,8 @@ export class SettingsComponent implements OnInit {
private analytics: Angulartics2, private vaultTimeoutService: VaultTimeoutService, private analytics: Angulartics2, private vaultTimeoutService: VaultTimeoutService,
private storageService: StorageService, public messagingService: MessagingService, private storageService: StorageService, public messagingService: MessagingService,
private router: Router, private environmentService: EnvironmentService, private router: Router, private environmentService: EnvironmentService,
private cryptoService: CryptoService, private userService: UserService) { private cryptoService: CryptoService, private userService: UserService,
private popupUtilsService: PopupUtilsService) {
} }
async ngOnInit() { async ngOnInit() {
@ -212,31 +214,30 @@ export class SettingsComponent implements OnInit {
async updateBiometric() { async updateBiometric() {
if (this.biometric && this.supportsBiometric) { if (this.biometric && this.supportsBiometric) {
// Request permission to use the optional permission for nativeMessaging let granted;
if (!this.platformUtilsService.isFirefox()) { try {
const hasPermission = await new Promise(resolve => { granted = await BrowserApi.requestPermission({ permissions: ['nativeMessaging'] });
chrome.permissions.contains({ permissions: ['nativeMessaging'] }, resolve); } catch (e) {
}); // tslint:disable-next-line
console.error(e);
if (!hasPermission) { if (this.platformUtilsService.isFirefox() && this.popupUtilsService.inSidebar(window)) {
await this.platformUtilsService.showDialog( await this.platformUtilsService.showDialog(
this.i18nService.t('nativeMessagingPermissionPromptDesc'), this.i18nService.t('nativeMessagingPermissionPromptTitle'), this.i18nService.t('nativeMessaginPermissionSidebarDesc'), this.i18nService.t('nativeMessaginPermissionSidebarTitle'),
this.i18nService.t('ok'), null); this.i18nService.t('ok'), null);
this.biometric = false;
const granted = await new Promise((resolve, reject) => { return;
chrome.permissions.request({ permissions: ['nativeMessaging'] }, resolve);
});
if (!granted) {
await this.platformUtilsService.showDialog(
this.i18nService.t('nativeMessaginPermissionErrorDesc'), this.i18nService.t('nativeMessaginPermissionErrorTitle'),
this.i18nService.t('ok'), null);
this.biometric = false;
return;
}
} }
} }
if (!granted) {
await this.platformUtilsService.showDialog(
this.i18nService.t('nativeMessaginPermissionErrorDesc'), this.i18nService.t('nativeMessaginPermissionErrorTitle'),
this.i18nService.t('ok'), null);
this.biometric = false;
return;
}
const submitted = Swal.fire({ const submitted = Swal.fire({
heightAuto: false, heightAuto: false,
buttonsStyling: false, buttonsStyling: false,

View File

@ -292,8 +292,9 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
}); });
} }
supportsBiometric() { async supportsBiometric() {
return Promise.resolve(!this.isFirefox() && !this.isSafari()); const isUnsuportedFirefox = this.isFirefox() && parseInt((await browser.runtime.getBrowserInfo()).version.split('.')[0], 10) < 87;
return !isUnsuportedFirefox && !this.isSafari();
} }
authenticateBiometric() { authenticateBiometric() {