mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 01:03:35 +00:00
[PM-990] Unix biometrics unlock via Polkit (#4586)
* Update unix biometrics for desktop biometrics rework * Implement polkit policy setup * Enable browser integration on Linux * Remove polkit policy file * Undo change to messages.json * Fix biometrics setup, implement missing functions * Implement osSupportsBiometrics * Fix polkit settings message * Remove unwraps in biometrics unix rust module * Force password reprompt on start on linux with biometrics * Merge branch 'main' into feature/unix-biometrics * Allow browser extension to be unlocked on Linux via Polkit * Implement availability check * Cleanup * Add auto-setup, manual setup, setup detection and change localized prompts * Implement missing methods * Add i18n to polkit message * Implement missing method * Small cleanup * Update polkit consent message * Fix unlock and print errors on failed biometrics * Add dependencies to core crate * Fix reference and update polkit policy * Remove async-trait * Add tsdoc * Add comment about auto setup * Delete unused init * Update help link * Remove additional settings for polkit * Add availability-check to passwords implementation on linux * Add availability test * Add availability check to libsecret * Expose availability check in napi crate * Update d.ts * Update osSupportsBiometric check to detect libsecret presence * Improve secret service detection * Add client half to Linux biometrics * Fix windows build * Remove unencrypted key handling for biometric key * Move rng to rust, align linux bio implementation with windows * Consolidate elevated commands into one * Disable snap support in linux biometrics --------- Co-authored-by: DigitallyRefined <129616584+DigitallyRefined@users.noreply.github.com>
This commit is contained in:
@@ -126,11 +126,14 @@
|
||||
{{ biometricText | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<small class="help-block" *ngIf="this.form.value.biometric">{{
|
||||
<small class="help-block" *ngIf="this.form.value.biometric && !this.isLinux">{{
|
||||
additionalBiometricSettingsText | i18n
|
||||
}}</small>
|
||||
</div>
|
||||
<div class="form-group" *ngIf="supportsBiometric && this.form.value.biometric">
|
||||
<div
|
||||
class="form-group"
|
||||
*ngIf="supportsBiometric && this.form.value.biometric && !this.isLinux"
|
||||
>
|
||||
<div class="checkbox form-group-child">
|
||||
<label for="autoPromptBiometrics">
|
||||
<input
|
||||
@@ -148,7 +151,8 @@
|
||||
*ngIf="
|
||||
supportsBiometric &&
|
||||
this.form.value.biometric &&
|
||||
(userHasMasterPassword || (this.form.value.pin && userHasPinSet))
|
||||
(userHasMasterPassword || (this.form.value.pin && userHasPinSet)) &&
|
||||
!this.isLinux
|
||||
"
|
||||
>
|
||||
<div class="checkbox form-group-child">
|
||||
|
||||
@@ -55,6 +55,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
requireEnableTray = false;
|
||||
showDuckDuckGoIntegrationOption = false;
|
||||
isWindows: boolean;
|
||||
isLinux: boolean;
|
||||
|
||||
enableTrayText: string;
|
||||
enableTrayDescText: string;
|
||||
@@ -197,6 +198,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
this.userHasMasterPassword = await this.userVerificationService.hasMasterPassword();
|
||||
|
||||
this.isWindows = (await this.platformUtilsService.getDevice()) === DeviceType.WindowsDesktop;
|
||||
this.isLinux = (await this.platformUtilsService.getDevice()) === DeviceType.LinuxDesktop;
|
||||
|
||||
if ((await this.stateService.getUserId()) == null) {
|
||||
return;
|
||||
@@ -464,6 +466,26 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const needsSetup = await this.platformUtilsService.biometricsNeedsSetup();
|
||||
const supportsBiometricAutoSetup =
|
||||
await this.platformUtilsService.biometricsSupportsAutoSetup();
|
||||
|
||||
if (needsSetup) {
|
||||
if (supportsBiometricAutoSetup) {
|
||||
await this.platformUtilsService.biometricsSetup();
|
||||
} else {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "biometricsManualSetupTitle" },
|
||||
content: { key: "biometricsManualSetupDesc" },
|
||||
type: "warning",
|
||||
});
|
||||
if (confirmed) {
|
||||
this.platformUtilsService.launchUri("https://bitwarden.com/help/biometrics/");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.biometricStateService.setBiometricUnlockEnabled(true);
|
||||
if (this.isWindows) {
|
||||
// Recommended settings for Windows Hello
|
||||
@@ -472,6 +494,13 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
await this.biometricStateService.setPromptAutomatically(false);
|
||||
await this.biometricStateService.setRequirePasswordOnStart(true);
|
||||
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
|
||||
} else if (this.isLinux) {
|
||||
// Similar to Windows
|
||||
this.form.controls.requirePasswordOnStart.setValue(true);
|
||||
this.form.controls.autoPromptBiometrics.setValue(false);
|
||||
await this.biometricStateService.setPromptAutomatically(false);
|
||||
await this.biometricStateService.setRequirePasswordOnStart(true);
|
||||
await this.biometricStateService.setDismissedRequirePasswordOnStartCallout();
|
||||
}
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
|
||||
@@ -624,7 +653,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.form.controls.enableBrowserIntegration.setValue(false);
|
||||
return;
|
||||
} else if (ipc.platform.deviceType === DeviceType.LinuxDesktop) {
|
||||
} else if (ipc.platform.isSnapStore || ipc.platform.isFlatpak) {
|
||||
await this.dialogService.openSimpleDialog({
|
||||
title: { key: "browserIntegrationUnsupportedTitle" },
|
||||
content: { key: "browserIntegrationLinuxDesc" },
|
||||
@@ -735,6 +764,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
return "unlockWithTouchId";
|
||||
case DeviceType.WindowsDesktop:
|
||||
return "unlockWithWindowsHello";
|
||||
case DeviceType.LinuxDesktop:
|
||||
return "unlockWithPolkit";
|
||||
default:
|
||||
throw new Error("Unsupported platform");
|
||||
}
|
||||
@@ -746,6 +777,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
return "autoPromptTouchId";
|
||||
case DeviceType.WindowsDesktop:
|
||||
return "autoPromptWindowsHello";
|
||||
case DeviceType.LinuxDesktop:
|
||||
return "autoPromptPolkit";
|
||||
default:
|
||||
throw new Error("Unsupported platform");
|
||||
}
|
||||
|
||||
@@ -217,6 +217,8 @@ export class LockComponent extends BaseLockComponent implements OnInit, OnDestro
|
||||
return "unlockWithTouchId";
|
||||
case DeviceType.WindowsDesktop:
|
||||
return "unlockWithWindowsHello";
|
||||
case DeviceType.LinuxDesktop:
|
||||
return "unlockWithPolkit";
|
||||
default:
|
||||
throw new Error("Unsupported platform");
|
||||
}
|
||||
|
||||
@@ -1510,9 +1510,15 @@
|
||||
"additionalWindowsHelloSettings": {
|
||||
"message": "Additional Windows Hello settings"
|
||||
},
|
||||
"unlockWithPolkit": {
|
||||
"message": "Unlock with system authentication"
|
||||
},
|
||||
"windowsHelloConsentMessage": {
|
||||
"message": "Verify for Bitwarden."
|
||||
},
|
||||
"polkitConsentMessage": {
|
||||
"message": "Authenticate to unlock Bitwarden."
|
||||
},
|
||||
"unlockWithTouchId": {
|
||||
"message": "Unlock with Touch ID"
|
||||
},
|
||||
@@ -1525,6 +1531,9 @@
|
||||
"autoPromptWindowsHello": {
|
||||
"message": "Ask for Windows Hello on app start"
|
||||
},
|
||||
"autoPromptPolkit": {
|
||||
"message": "Ask for system authentication on launch"
|
||||
},
|
||||
"autoPromptTouchId": {
|
||||
"message": "Ask for Touch ID on app start"
|
||||
},
|
||||
@@ -1804,6 +1813,12 @@
|
||||
"biometricsNotEnabledDesc": {
|
||||
"message": "Browser biometrics requires desktop biometrics to be set up in the settings first."
|
||||
},
|
||||
"biometricsManualSetupTitle": {
|
||||
"message": "Autometic setup not available"
|
||||
},
|
||||
"biometricsManualSetupDesc": {
|
||||
"message": "Due to the installation method, biometrics support could not be automatically enabled. Would you like to open the documentation on how to do this manually?"
|
||||
},
|
||||
"personalOwnershipSubmitError": {
|
||||
"message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections."
|
||||
},
|
||||
|
||||
@@ -51,4 +51,14 @@ export default class BiometricDarwinMain implements OsBiometricService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsSetup(): Promise<void> {}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,16 @@ export default class NoopBiometricsService implements OsBiometricService {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsSetup(): Promise<void> {}
|
||||
|
||||
async getBiometricKey(
|
||||
service: string,
|
||||
storageKey: string,
|
||||
|
||||
160
apps/desktop/src/platform/main/biometric/biometric.unix.main.ts
Normal file
160
apps/desktop/src/platform/main/biometric/biometric.unix.main.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { spawn } from "child_process";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { biometrics, passwords } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
import { isFlatpak, isLinux, isSnapStore } from "../../../utils";
|
||||
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
|
||||
const polkitPolicy = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
||||
|
||||
<policyconfig>
|
||||
<action id="com.bitwarden.Bitwarden.unlock">
|
||||
<description>Unlock Bitwarden</description>
|
||||
<message>Authenticate to unlock Bitwarden</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>auth_self</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>`;
|
||||
const policyFileName = "com.bitwarden.Bitwarden.policy";
|
||||
const policyPath = "/usr/share/polkit-1/actions/";
|
||||
|
||||
export default class BiometricUnixMain implements OsBiometricService {
|
||||
constructor(
|
||||
private i18nservice: I18nService,
|
||||
private windowMain: WindowMain,
|
||||
) {}
|
||||
private _iv: string | null = null;
|
||||
// Use getKeyMaterial helper instead of direct access
|
||||
private _osKeyHalf: string | null = null;
|
||||
|
||||
async setBiometricKey(
|
||||
service: string,
|
||||
key: string,
|
||||
value: string,
|
||||
clientKeyPartB64: string | undefined,
|
||||
): Promise<void> {
|
||||
const storageDetails = await this.getStorageDetails({ clientKeyHalfB64: clientKeyPartB64 });
|
||||
await biometrics.setBiometricSecret(
|
||||
service,
|
||||
key,
|
||||
value,
|
||||
storageDetails.key_material,
|
||||
storageDetails.ivB64,
|
||||
);
|
||||
}
|
||||
async deleteBiometricKey(service: string, key: string): Promise<void> {
|
||||
await passwords.deletePassword(service, key);
|
||||
}
|
||||
|
||||
async getBiometricKey(
|
||||
service: string,
|
||||
storageKey: string,
|
||||
clientKeyPartB64: string | undefined,
|
||||
): Promise<string | null> {
|
||||
const success = await this.authenticateBiometric();
|
||||
|
||||
if (!success) {
|
||||
throw new Error("Biometric authentication failed");
|
||||
}
|
||||
|
||||
const value = await passwords.getPassword(service, storageKey);
|
||||
|
||||
if (value == null || value == "") {
|
||||
return null;
|
||||
} else {
|
||||
const encValue = new EncString(value);
|
||||
this.setIv(encValue.iv);
|
||||
const storageDetails = await this.getStorageDetails({ clientKeyHalfB64: clientKeyPartB64 });
|
||||
const storedValue = await biometrics.getBiometricSecret(
|
||||
service,
|
||||
storageKey,
|
||||
storageDetails.key_material,
|
||||
);
|
||||
return storedValue;
|
||||
}
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
const hwnd = this.windowMain.win.getNativeWindowHandle();
|
||||
return await biometrics.prompt(hwnd, this.i18nservice.t("polkitConsentMessage"));
|
||||
}
|
||||
|
||||
async osSupportsBiometric(): Promise<boolean> {
|
||||
// We assume all linux distros have some polkit implementation
|
||||
// that either has bitwarden set up or not, which is reflected in osBiomtricsNeedsSetup.
|
||||
// Snap does not have access at the moment to polkit
|
||||
// This could be dynamically detected on dbus in the future.
|
||||
// We should check if a libsecret implementation is available on the system
|
||||
// because otherwise we cannot offlod the protected userkey to secure storage.
|
||||
return (await passwords.isAvailable()) && !isSnapStore();
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup(): Promise<boolean> {
|
||||
// check whether the polkit policy is loaded via dbus call to polkit
|
||||
return !(await biometrics.available());
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup(): Promise<boolean> {
|
||||
// We cannot auto setup on snap or flatpak since the filesystem is sandboxed.
|
||||
// The user needs to manually set up the polkit policy outside of the sandbox
|
||||
// since we allow access to polkit via dbus for the sandboxed clients, the authentication works from
|
||||
// the sandbox, once the policy is set up outside of the sandbox.
|
||||
return isLinux() && !isSnapStore() && !isFlatpak();
|
||||
}
|
||||
|
||||
async osBiometricsSetup(): Promise<void> {
|
||||
const process = spawn("pkexec", [
|
||||
"bash",
|
||||
"-c",
|
||||
`echo '${polkitPolicy}' > ${policyPath + policyFileName} && chown root:root ${policyPath + policyFileName} && chcon system_u:object_r:usr_t:s0 ${policyPath + policyFileName}`,
|
||||
]);
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
process.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject("Failed to set up polkit policy");
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Nulls out key material in order to force a re-derive. This should only be used in getBiometricKey
|
||||
// when we want to force a re-derive of the key material.
|
||||
private setIv(iv: string) {
|
||||
this._iv = iv;
|
||||
this._osKeyHalf = null;
|
||||
}
|
||||
|
||||
private async getStorageDetails({
|
||||
clientKeyHalfB64,
|
||||
}: {
|
||||
clientKeyHalfB64: string;
|
||||
}): Promise<{ key_material: biometrics.KeyMaterial; ivB64: string }> {
|
||||
if (this._osKeyHalf == null) {
|
||||
const keyMaterial = await biometrics.deriveKeyMaterial(this._iv);
|
||||
// osKeyHalf is based on the iv and in contrast to windows is not locked behind user verefication!
|
||||
this._osKeyHalf = keyMaterial.keyB64;
|
||||
this._iv = keyMaterial.ivB64;
|
||||
}
|
||||
|
||||
return {
|
||||
key_material: {
|
||||
osKeyPartB64: this._osKeyHalf,
|
||||
clientKeyPartB64: clientKeyHalfB64,
|
||||
},
|
||||
ivB64: this._iv,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -214,4 +214,14 @@ export default class BiometricWindowsMain implements OsBiometricService {
|
||||
clientKeyPartB64,
|
||||
};
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async osBiometricsSetup(): Promise<void> {}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export abstract class BiometricsServiceAbstraction {
|
||||
abstract osSupportsBiometric(): Promise<boolean>;
|
||||
abstract osBiometricsNeedsSetup: () => Promise<boolean>;
|
||||
abstract osBiometricsCanAutoSetup: () => Promise<boolean>;
|
||||
abstract osBiometricsSetup: () => Promise<void>;
|
||||
abstract canAuthBiometric({
|
||||
service,
|
||||
key,
|
||||
@@ -26,6 +29,22 @@ export abstract class BiometricsServiceAbstraction {
|
||||
|
||||
export interface OsBiometricService {
|
||||
osSupportsBiometric(): Promise<boolean>;
|
||||
/**
|
||||
* Check whether support for biometric unlock requires setup. This can be automatic or manual.
|
||||
*
|
||||
* @returns true if biometrics support requires setup, false if it does not (is already setup, or did not require it in the first place)
|
||||
*/
|
||||
osBiometricsNeedsSetup: () => Promise<boolean>;
|
||||
/**
|
||||
* Check whether biometrics can be automatically setup, or requires user interaction.
|
||||
*
|
||||
* @returns true if biometrics support can be automatically setup, false if it requires user interaction.
|
||||
*/
|
||||
osBiometricsCanAutoSetup: () => Promise<boolean>;
|
||||
/**
|
||||
* Starts automatic biometric setup, which places the required configuration files / changes the required settings.
|
||||
*/
|
||||
osBiometricsSetup: () => Promise<void>;
|
||||
authenticateBiometric(): Promise<boolean>;
|
||||
getBiometricKey(
|
||||
service: string,
|
||||
|
||||
@@ -28,6 +28,8 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
this.loadWindowsHelloService();
|
||||
} else if (platform === "darwin") {
|
||||
this.loadMacOSService();
|
||||
} else if (platform === "linux") {
|
||||
this.loadUnixService();
|
||||
} else {
|
||||
this.loadNoopBiometricsService();
|
||||
}
|
||||
@@ -49,6 +51,12 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
this.platformSpecificService = new BiometricDarwinMain(this.i18nService);
|
||||
}
|
||||
|
||||
private loadUnixService() {
|
||||
// eslint-disable-next-line
|
||||
const BiometricUnixMain = require("./biometric.unix.main").default;
|
||||
this.platformSpecificService = new BiometricUnixMain(this.i18nService, this.windowMain);
|
||||
}
|
||||
|
||||
private loadNoopBiometricsService() {
|
||||
// eslint-disable-next-line
|
||||
const NoopBiometricsService = require("./biometric.noop.main").default;
|
||||
@@ -59,6 +67,18 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
return await this.platformSpecificService.osSupportsBiometric();
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup() {
|
||||
return await this.platformSpecificService.osBiometricsNeedsSetup();
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup() {
|
||||
return await this.platformSpecificService.osBiometricsCanAutoSetup();
|
||||
}
|
||||
|
||||
async osBiometricsSetup() {
|
||||
await this.platformSpecificService.osBiometricsSetup();
|
||||
}
|
||||
|
||||
async canAuthBiometric({
|
||||
service,
|
||||
key,
|
||||
|
||||
@@ -79,6 +79,15 @@ export class DesktopCredentialStorageListener {
|
||||
case BiometricAction.OsSupported:
|
||||
val = await this.biometricService.osSupportsBiometric();
|
||||
break;
|
||||
case BiometricAction.NeedsSetup:
|
||||
val = await this.biometricService.osBiometricsNeedsSetup();
|
||||
break;
|
||||
case BiometricAction.Setup:
|
||||
await this.biometricService.osBiometricsSetup();
|
||||
break;
|
||||
case BiometricAction.CanAutoSetup:
|
||||
val = await this.biometricService.osBiometricsCanAutoSetup();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
UnencryptedMessageResponse,
|
||||
} from "../models/native-messaging";
|
||||
import { BiometricMessage, BiometricAction } from "../types/biometric-message";
|
||||
import { isDev, isMacAppStore, isWindowsStore } from "../utils";
|
||||
import { isDev, isFlatpak, isMacAppStore, isSnapStore, isWindowsStore } from "../utils";
|
||||
|
||||
import { ClipboardWriteMessage } from "./types/clipboard";
|
||||
|
||||
@@ -48,6 +48,18 @@ const biometric = {
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.OsSupported,
|
||||
} satisfies BiometricMessage),
|
||||
biometricsNeedsSetup: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.NeedsSetup,
|
||||
} satisfies BiometricMessage),
|
||||
biometricsSetup: (): Promise<void> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.Setup,
|
||||
} satisfies BiometricMessage),
|
||||
biometricsCanAutoSetup: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.CanAutoSetup,
|
||||
} satisfies BiometricMessage),
|
||||
authenticate: (): Promise<boolean> =>
|
||||
ipcRenderer.invoke("biometric", {
|
||||
action: BiometricAction.Authenticate,
|
||||
@@ -115,6 +127,8 @@ export default {
|
||||
isDev: isDev(),
|
||||
isMacAppStore: isMacAppStore(),
|
||||
isWindowsStore: isWindowsStore(),
|
||||
isFlatpak: isFlatpak(),
|
||||
isSnapStore: isSnapStore(),
|
||||
reloadProcess: () => ipcRenderer.send("reload-process"),
|
||||
log: (level: LogLevelType, message?: any, ...optionalParams: any[]) =>
|
||||
ipcRenderer.invoke("ipc.log", { level, message, optionalParams }),
|
||||
|
||||
@@ -135,6 +135,18 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
return await ipc.platform.biometric.osSupported();
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsNeedsSetup();
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsCanAutoSetup();
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {
|
||||
return await ipc.platform.biometric.biometricsSetup();
|
||||
}
|
||||
|
||||
/** This method is used to authenticate the user presence _only_.
|
||||
* It should not be used in the process to retrieve
|
||||
* biometric keys, which has a separate authentication mechanism.
|
||||
|
||||
@@ -2,6 +2,9 @@ export enum BiometricAction {
|
||||
EnabledForUser = "enabled",
|
||||
OsSupported = "osSupported",
|
||||
Authenticate = "authenticate",
|
||||
NeedsSetup = "needsSetup",
|
||||
Setup = "setup",
|
||||
CanAutoSetup = "canAutoSetup",
|
||||
}
|
||||
|
||||
export type BiometricMessage = {
|
||||
|
||||
@@ -62,6 +62,10 @@ export function isWindowsStore() {
|
||||
return windows && windowsStore === true;
|
||||
}
|
||||
|
||||
export function isFlatpak() {
|
||||
return process.platform === "linux" && process.env.container != null;
|
||||
}
|
||||
|
||||
export function isWindowsPortable() {
|
||||
return isWindows() && process.env.PORTABLE_EXECUTABLE_DIR != null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user