mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 14:53:33 +00:00
Remove keytar and biometric logic (#706)
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
export abstract class BiometricMain {
|
|
||||||
isError: boolean;
|
|
||||||
init: () => Promise<void>;
|
|
||||||
supportsBiometric: () => Promise<boolean>;
|
|
||||||
authenticateBiometric: () => Promise<boolean>;
|
|
||||||
}
|
|
||||||
963
electron/package-lock.json
generated
963
electron/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,12 +24,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/jslib-common": "file:../common",
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
|
||||||
"electron": "16.1.0",
|
"electron": "16.1.0",
|
||||||
"electron-log": "4.4.6",
|
"electron-log": "4.4.6",
|
||||||
"electron-store": "8.0.1",
|
"electron-store": "8.0.1",
|
||||||
"electron-updater": "5.0.0",
|
"electron-updater": "5.0.0"
|
||||||
"forcefocus": "^1.1.0",
|
|
||||||
"keytar": "7.7.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import { ipcMain, systemPreferences } from "electron";
|
|
||||||
|
|
||||||
import { BiometricMain } from "jslib-common/abstractions/biometric.main";
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
|
||||||
|
|
||||||
export default class BiometricDarwinMain implements BiometricMain {
|
|
||||||
isError = false;
|
|
||||||
|
|
||||||
constructor(private i18nservice: I18nService, private stateService: StateService) {}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
await this.stateService.setEnableBiometric(await this.supportsBiometric());
|
|
||||||
await this.stateService.setBiometricText("unlockWithTouchId");
|
|
||||||
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptTouchId");
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
ipcMain.on("biometric", async (event: any, message: any) => {
|
|
||||||
event.returnValue = await this.authenticateBiometric();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
supportsBiometric(): Promise<boolean> {
|
|
||||||
return Promise.resolve(systemPreferences.canPromptTouchID());
|
|
||||||
}
|
|
||||||
|
|
||||||
async authenticateBiometric(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
await systemPreferences.promptTouchID(this.i18nservice.t("touchIdConsentMessage"));
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
import { ipcMain } from "electron";
|
|
||||||
import forceFocus from "forcefocus";
|
|
||||||
|
|
||||||
import { BiometricMain } from "jslib-common/abstractions/biometric.main";
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
|
||||||
|
|
||||||
import { WindowMain } from "./window.main";
|
|
||||||
|
|
||||||
export default class BiometricWindowsMain implements BiometricMain {
|
|
||||||
isError = false;
|
|
||||||
|
|
||||||
private windowsSecurityCredentialsUiModule: any;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private i18nservice: I18nService,
|
|
||||||
private windowMain: WindowMain,
|
|
||||||
private stateService: StateService,
|
|
||||||
private logService: LogService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
this.windowsSecurityCredentialsUiModule = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
let supportsBiometric = false;
|
|
||||||
try {
|
|
||||||
supportsBiometric = await this.supportsBiometric();
|
|
||||||
} catch {
|
|
||||||
// store error state so we can let the user know on the settings page
|
|
||||||
this.isError = true;
|
|
||||||
}
|
|
||||||
await this.stateService.setEnableBiometric(supportsBiometric);
|
|
||||||
await this.stateService.setBiometricText("unlockWithWindowsHello");
|
|
||||||
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptWindowsHello");
|
|
||||||
|
|
||||||
ipcMain.on("biometric", async (event: any, message: any) => {
|
|
||||||
event.returnValue = await this.authenticateBiometric();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async supportsBiometric(): Promise<boolean> {
|
|
||||||
const availability = await this.checkAvailabilityAsync();
|
|
||||||
|
|
||||||
return this.getAllowedAvailabilities().includes(availability);
|
|
||||||
}
|
|
||||||
|
|
||||||
async authenticateBiometric(): Promise<boolean> {
|
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
if (module == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const verification = await this.requestVerificationAsync(
|
|
||||||
this.i18nservice.t("windowsHelloConsentMessage")
|
|
||||||
);
|
|
||||||
|
|
||||||
return verification === module.UserConsentVerificationResult.verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
getWindowsSecurityCredentialsUiModule(): any {
|
|
||||||
try {
|
|
||||||
if (this.windowsSecurityCredentialsUiModule == null && this.getWindowsMajorVersion() >= 10) {
|
|
||||||
this.windowsSecurityCredentialsUiModule = require("@nodert-win10-rs4/windows.security.credentials.ui");
|
|
||||||
}
|
|
||||||
return this.windowsSecurityCredentialsUiModule;
|
|
||||||
} catch {
|
|
||||||
this.isError = true;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkAvailabilityAsync(): Promise<any> {
|
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
if (module != null) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
module.UserConsentVerifier.checkAvailabilityAsync((error: Error, result: any) => {
|
|
||||||
if (error) {
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
return resolve(result);
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
this.isError = true;
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
async requestVerificationAsync(message: string): Promise<any> {
|
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
if (module != null) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
module.UserConsentVerifier.requestVerificationAsync(
|
|
||||||
message,
|
|
||||||
(error: Error, result: any) => {
|
|
||||||
if (error) {
|
|
||||||
return resolve(null);
|
|
||||||
}
|
|
||||||
return resolve(result);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
forceFocus.focusWindow(this.windowMain.win);
|
|
||||||
} catch (error) {
|
|
||||||
this.isError = true;
|
|
||||||
return reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllowedAvailabilities(): any[] {
|
|
||||||
try {
|
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
if (module != null) {
|
|
||||||
return [
|
|
||||||
module.UserConsentVerifierAvailability.available,
|
|
||||||
module.UserConsentVerifierAvailability.deviceBusy,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
/*Ignore error*/
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getWindowsMajorVersion(): number {
|
|
||||||
if (process.platform !== "win32") {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const version = require("os").release();
|
|
||||||
return Number.parseInt(version.split(".")[0], 10);
|
|
||||||
} catch {
|
|
||||||
this.logService.error("Unable to resolve windows major version number");
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
electron/src/globals.d.ts
vendored
1
electron/src/globals.d.ts
vendored
@@ -1 +0,0 @@
|
|||||||
declare module "forcefocus";
|
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import { ipcMain } from "electron";
|
|
||||||
import { deletePassword, getPassword, setPassword } from "keytar";
|
|
||||||
|
|
||||||
import { BiometricMain } from "jslib-common/abstractions/biometric.main";
|
|
||||||
|
|
||||||
const AuthRequiredSuffix = "_biometric";
|
|
||||||
const AuthenticatedActions = ["getPassword"];
|
|
||||||
|
|
||||||
export class KeytarStorageListener {
|
|
||||||
constructor(private serviceName: string, private biometricService: BiometricMain) {}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
ipcMain.on("keytar", async (event: any, message: any) => {
|
|
||||||
try {
|
|
||||||
let serviceName = this.serviceName;
|
|
||||||
message.keySuffix = "_" + (message.keySuffix ?? "");
|
|
||||||
if (message.keySuffix !== "_") {
|
|
||||||
serviceName += message.keySuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
const authenticationRequired =
|
|
||||||
AuthenticatedActions.includes(message.action) && AuthRequiredSuffix === message.keySuffix;
|
|
||||||
const authenticated = !authenticationRequired || (await this.authenticateBiometric());
|
|
||||||
|
|
||||||
let val: string | boolean = null;
|
|
||||||
if (authenticated && message.action && message.key) {
|
|
||||||
if (message.action === "getPassword") {
|
|
||||||
val = await getPassword(serviceName, message.key);
|
|
||||||
} else if (message.action === "hasPassword") {
|
|
||||||
const result = await getPassword(serviceName, message.key);
|
|
||||||
val = result != null;
|
|
||||||
} else if (message.action === "setPassword" && message.value) {
|
|
||||||
await setPassword(serviceName, message.key, message.value);
|
|
||||||
} else if (message.action === "deletePassword") {
|
|
||||||
await deletePassword(serviceName, message.key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event.returnValue = val;
|
|
||||||
} catch {
|
|
||||||
event.returnValue = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async authenticateBiometric(): Promise<boolean> {
|
|
||||||
if (this.biometricService) {
|
|
||||||
return await this.biometricService.authenticateBiometric();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25227
package-lock.json
generated
25227
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,9 @@
|
|||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^12.2.13",
|
||||||
|
"@angular/compiler-cli": "^12.2.13",
|
||||||
|
"@angular/platform-browser-dynamic": "^12.2.13",
|
||||||
"@fluffy-spoon/substitute": "^1.202.0",
|
"@fluffy-spoon/substitute": "^1.202.0",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/node": "^16.11.12",
|
"@types/node": "^16.11.12",
|
||||||
@@ -40,6 +43,7 @@
|
|||||||
"jest-preset-angular": "^11.1.1",
|
"jest-preset-angular": "^11.1.1",
|
||||||
"jsdom": "^16.5.3",
|
"jsdom": "^16.5.3",
|
||||||
"lint-staged": "^12.1.2",
|
"lint-staged": "^12.1.2",
|
||||||
|
"node-forge": "^1.2.0",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.7",
|
||||||
"prettier": "2.5.1",
|
"prettier": "2.5.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
@@ -51,7 +55,7 @@
|
|||||||
"typemoq": "^2.1.0",
|
"typemoq": "^2.1.0",
|
||||||
"typescript": "4.3.5",
|
"typescript": "4.3.5",
|
||||||
"typescript-transform-paths": "^2.2.3",
|
"typescript-transform-paths": "^2.2.3",
|
||||||
"webpack": "^4.46.0"
|
"zone.js": "0.11.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/jslib-angular": "file:angular",
|
"@bitwarden/jslib-angular": "file:angular",
|
||||||
|
|||||||
Reference in New Issue
Block a user