mirror of
https://github.com/bitwarden/desktop.git
synced 2024-12-25 16:47:55 +01:00
Switch to our own rust based windows hello
This commit is contained in:
parent
865e92c94c
commit
97d6b4bac7
1
desktop_native/.gitignore
vendored
1
desktop_native/.gitignore
vendored
@ -3,3 +3,4 @@ index.node
|
|||||||
**/node_modules
|
**/node_modules
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
*.node
|
||||||
|
@ -25,9 +25,11 @@ widestring = "0.5.1"
|
|||||||
windows = {version = "0.32.0", features = [
|
windows = {version = "0.32.0", features = [
|
||||||
"alloc",
|
"alloc",
|
||||||
"Foundation",
|
"Foundation",
|
||||||
|
"Security_Credentials_UI",
|
||||||
"Storage_Streams",
|
"Storage_Streams",
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Security_Credentials",
|
"Win32_Security_Credentials",
|
||||||
|
"Win32_System_WinRT",
|
||||||
]}
|
]}
|
||||||
|
|
||||||
[target.'cfg(windows)'.dev-dependencies]
|
[target.'cfg(windows)'.dev-dependencies]
|
||||||
|
4
desktop_native/index.d.ts
vendored
4
desktop_native/index.d.ts
vendored
@ -13,3 +13,7 @@ export namespace passwords {
|
|||||||
/** Delete the stored password from the keychain. */
|
/** Delete the stored password from the keychain. */
|
||||||
export function deletePassword(service: string, account: string): Promise<void>
|
export function deletePassword(service: string, account: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
export namespace biometrics {
|
||||||
|
export function prompt(hwnd: Buffer, message: string): Promise<boolean>
|
||||||
|
export function available(): Promise<boolean>
|
||||||
|
}
|
||||||
|
@ -1,32 +1,97 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
const { existsSync, readFileSync } = require('fs')
|
||||||
const { readFileSync } = require('fs')
|
const { join } = require('path')
|
||||||
|
|
||||||
const { platform, arch } = process
|
const { platform, arch } = process
|
||||||
|
|
||||||
let nativeBinding = null
|
let nativeBinding = null
|
||||||
let isMusl = false
|
let localFileExisted = false
|
||||||
let loadError = null
|
let loadError = null
|
||||||
|
|
||||||
|
function isMusl() {
|
||||||
|
// For Node 10
|
||||||
|
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||||
|
try {
|
||||||
|
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
|
||||||
|
} catch (e) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { glibcVersionRuntime } = process.report.getReport().header
|
||||||
|
return !glibcVersionRuntime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
|
case 'android':
|
||||||
|
switch (arch) {
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm64.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.android-arm64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-android-arm64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'arm':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'desktop_native.android-arm-eabi.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.android-arm-eabi.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-android-arm-eabi')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Android ${arch}`)
|
||||||
|
}
|
||||||
|
break
|
||||||
case 'win32':
|
case 'win32':
|
||||||
switch (arch) {
|
switch (arch) {
|
||||||
case 'x64':
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.win32-x64-msvc.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.win32-x64-msvc')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.win32-x64-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-win32-x64-msvc')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ia32':
|
case 'ia32':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.win32-ia32-msvc.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.win32-ia32-msvc')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.win32-ia32-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-win32-ia32-msvc')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'arm64':
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.win32-arm64-msvc.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.win32-arm64-msvc')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.win32-arm64-msvc.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-win32-arm64-msvc')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
@ -38,15 +103,27 @@ switch (platform) {
|
|||||||
case 'darwin':
|
case 'darwin':
|
||||||
switch (arch) {
|
switch (arch) {
|
||||||
case 'x64':
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'desktop_native.darwin-x64.node'))
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.darwin-x64')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.darwin-x64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-darwin-x64')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'arm64':
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.darwin-arm64.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.darwin-arm64')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.darwin-arm64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-darwin-arm64')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
@ -55,43 +132,91 @@ switch (platform) {
|
|||||||
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case 'freebsd':
|
||||||
|
if (arch !== 'x64') {
|
||||||
|
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
||||||
|
}
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'desktop_native.freebsd-x64.node'))
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.freebsd-x64.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-freebsd-x64')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e
|
||||||
|
}
|
||||||
|
break
|
||||||
case 'linux':
|
case 'linux':
|
||||||
switch (arch) {
|
switch (arch) {
|
||||||
case 'x64':
|
case 'x64':
|
||||||
isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
|
if (isMusl()) {
|
||||||
if (isMusl) {
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.linux-x64-musl.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.linux-x64-musl')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.linux-x64-musl.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-linux-x64-musl')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.linux-x64-gnu.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.linux-x64-gnu')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.linux-x64-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-linux-x64-gnu')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'arm64':
|
case 'arm64':
|
||||||
isMusl = readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
|
if (isMusl()) {
|
||||||
if (isMusl) {
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.linux-arm64-musl.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.linux-arm64-musl')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.linux-arm64-musl.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-linux-arm64-musl')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.linux-arm64-gnu.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.linux-arm64-gnu')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.linux-arm64-gnu.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-linux-arm64-gnu')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'arm':
|
case 'arm':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'desktop_native.linux-arm-gnueabihf.node')
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
nativeBinding = require('./dist/desktop_native.linux-arm-gnueabihf')
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./desktop_native.linux-arm-gnueabihf.node')
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@bitwarden/desktop-native-linux-arm-gnueabihf')
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadError = e
|
loadError = e
|
||||||
}
|
}
|
||||||
@ -111,4 +236,7 @@ if (!nativeBinding) {
|
|||||||
throw new Error(`Failed to load native binding`)
|
throw new Error(`Failed to load native binding`)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.nativeBinding
|
const { passwords, biometrics } = nativeBinding
|
||||||
|
|
||||||
|
module.exports.passwords = passwords
|
||||||
|
module.exports.biometrics = biometrics
|
||||||
|
5
desktop_native/package-lock.json
generated
5
desktop_native/package-lock.json
generated
@ -1,13 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop_native",
|
"name": "@bitwarden/desktop-native",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@bitwarden/desktop_native",
|
"name": "@bitwarden/desktop-native",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@napi-rs/cli": "^2.6.2"
|
"@napi-rs/cli": "^2.6.2"
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop_native",
|
"name": "@bitwarden/desktop-native",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.node",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "napi build dist --platform --release --js true",
|
"build": "napi build --platform --release",
|
||||||
"build:debug": "napi build dist --platform --js true",
|
"build:debug": "napi build --platform",
|
||||||
"build:cross-platform": "node build.js",
|
"build:cross-platform": "node build.js",
|
||||||
"test": "cargo test"
|
"test": "cargo test"
|
||||||
},
|
},
|
||||||
|
5
desktop_native/src/biometric/mod.rs
Normal file
5
desktop_native/src/biometric/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#[cfg_attr(target_os = "linux", path = "unix.rs")]
|
||||||
|
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
||||||
|
#[cfg_attr(target_os = "macos", path = "macos.rs")]
|
||||||
|
mod biometric;
|
||||||
|
pub use biometric::*;
|
51
desktop_native/src/biometric/windows.rs
Normal file
51
desktop_native/src/biometric/windows.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use windows::{
|
||||||
|
core::factory, Foundation::IAsyncOperation, Security::Credentials::UI::*,
|
||||||
|
Win32::Foundation::HWND, Win32::System::WinRT::IUserConsentVerifierInterop,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn prompt(hwnd: Vec<u8>, message: String) -> Result<bool> {
|
||||||
|
let interop = factory::<UserConsentVerifier, IUserConsentVerifierInterop>()?;
|
||||||
|
|
||||||
|
let h = isize::from_le_bytes(hwnd.try_into().unwrap());
|
||||||
|
let window = HWND(h);
|
||||||
|
|
||||||
|
let operation: IAsyncOperation<UserConsentVerificationResult> =
|
||||||
|
unsafe { interop.RequestVerificationForWindowAsync(window, message)? };
|
||||||
|
|
||||||
|
let result: UserConsentVerificationResult = operation.get()?;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
UserConsentVerificationResult::Verified => Ok(true),
|
||||||
|
_ => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available() -> Result<bool> {
|
||||||
|
let ucv_available = UserConsentVerifier::CheckAvailabilityAsync()?.get()?;
|
||||||
|
|
||||||
|
match ucv_available {
|
||||||
|
UserConsentVerifierAvailability::Available => Ok(true),
|
||||||
|
UserConsentVerifierAvailability::DeviceBusy => Ok(true), // TODO: Look into removing this and making the check more ad-hoc
|
||||||
|
_ => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prompt() {
|
||||||
|
prompt(
|
||||||
|
vec![0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
String::from("Hello from Rust"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_available() {
|
||||||
|
assert!(available().unwrap())
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
||||||
|
|
||||||
|
mod biometric;
|
||||||
mod password;
|
mod password;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
@ -37,3 +38,21 @@ pub mod passwords {
|
|||||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub mod biometrics {
|
||||||
|
// Prompt for biometric confirmation
|
||||||
|
#[napi]
|
||||||
|
pub async fn prompt(
|
||||||
|
hwnd: napi::bindgen_prelude::Buffer,
|
||||||
|
message: String,
|
||||||
|
) -> napi::Result<bool> {
|
||||||
|
super::biometric::prompt(hwnd.into(), message)
|
||||||
|
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub async fn available() -> napi::Result<bool> {
|
||||||
|
super::biometric::available().map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1286
package-lock.json
generated
1286
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -84,7 +84,7 @@
|
|||||||
"**/*",
|
"**/*",
|
||||||
"!**/node_modules/@bitwarden/desktop-native/**/*",
|
"!**/node_modules/@bitwarden/desktop-native/**/*",
|
||||||
"**/node_modules/@bitwarden/desktop-native/index.js",
|
"**/node_modules/@bitwarden/desktop-native/index.js",
|
||||||
"**/node_modules/@bitwarden/desktop-native/dist/desktop_native.${platform}-${arch}*.node"
|
"**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node"
|
||||||
],
|
],
|
||||||
"mac": {
|
"mac": {
|
||||||
"electronUpdaterCompatibility": ">=0.0.1",
|
"electronUpdaterCompatibility": ">=0.0.1",
|
||||||
@ -314,8 +314,6 @@
|
|||||||
"@bitwarden/jslib-angular": "file:jslib/angular",
|
"@bitwarden/jslib-angular": "file:jslib/angular",
|
||||||
"@bitwarden/jslib-common": "file:jslib/common",
|
"@bitwarden/jslib-common": "file:jslib/common",
|
||||||
"@bitwarden/jslib-electron": "file:jslib/electron",
|
"@bitwarden/jslib-electron": "file:jslib/electron",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
|
||||||
"forcefocus": "^1.1.0",
|
|
||||||
"ngx-toastr": "14.1.4",
|
"ngx-toastr": "14.1.4",
|
||||||
"node-ipc": "^9.1.4",
|
"node-ipc": "^9.1.4",
|
||||||
"nord": "^0.2.1",
|
"nord": "^0.2.1",
|
||||||
|
@ -6,8 +6,6 @@ import { StateService } from "jslib-common/abstractions/state.service";
|
|||||||
import { BiometricMain } from "../biometric/biometric.main";
|
import { BiometricMain } from "../biometric/biometric.main";
|
||||||
|
|
||||||
export default class BiometricDarwinMain implements BiometricMain {
|
export default class BiometricDarwinMain implements BiometricMain {
|
||||||
isError = false;
|
|
||||||
|
|
||||||
constructor(private i18nservice: I18nService, private stateService: StateService) {}
|
constructor(private i18nservice: I18nService, private stateService: StateService) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export abstract class BiometricMain {
|
export abstract class BiometricMain {
|
||||||
isError: boolean;
|
|
||||||
init: () => Promise<void>;
|
init: () => Promise<void>;
|
||||||
supportsBiometric: () => Promise<boolean>;
|
supportsBiometric: () => Promise<boolean>;
|
||||||
authenticateBiometric: () => Promise<boolean>;
|
authenticateBiometric: () => Promise<boolean>;
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
|
import { biometrics } from "@bitwarden/desktop-native";
|
||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import forceFocus from "forcefocus";
|
|
||||||
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
import { WindowMain } from "jslib-electron/window.main";
|
import { WindowMain } from "jslib-electron/window.main";
|
||||||
|
|
||||||
|
|
||||||
import { BiometricMain } from "src/main/biometric/biometric.main";
|
import { BiometricMain } from "src/main/biometric/biometric.main";
|
||||||
|
|
||||||
export default class BiometricWindowsMain implements BiometricMain {
|
export default class BiometricWindowsMain implements BiometricMain {
|
||||||
isError = false;
|
|
||||||
|
|
||||||
private windowsSecurityCredentialsUiModule: any;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private i18nservice: I18nService,
|
private i18nservice: I18nService,
|
||||||
private windowMain: WindowMain,
|
private windowMain: WindowMain,
|
||||||
@ -21,126 +18,32 @@ export default class BiometricWindowsMain implements BiometricMain {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
this.windowsSecurityCredentialsUiModule = this.getWindowsSecurityCredentialsUiModule();
|
|
||||||
let supportsBiometric = false;
|
let supportsBiometric = false;
|
||||||
try {
|
try {
|
||||||
supportsBiometric = await this.supportsBiometric();
|
supportsBiometric = await this.supportsBiometric();
|
||||||
} catch {
|
} catch (e) {
|
||||||
// store error state so we can let the user know on the settings page
|
this.logService.error(e);
|
||||||
this.isError = true;
|
|
||||||
}
|
}
|
||||||
await this.stateService.setEnableBiometric(supportsBiometric);
|
await this.stateService.setEnableBiometric(supportsBiometric);
|
||||||
await this.stateService.setBiometricText("unlockWithWindowsHello");
|
await this.stateService.setBiometricText("unlockWithWindowsHello");
|
||||||
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptWindowsHello");
|
await this.stateService.setNoAutoPromptBiometricsText("noAutoPromptWindowsHello");
|
||||||
|
|
||||||
ipcMain.on("biometric", async (event: any, message: any) => {
|
ipcMain.handle("biometric", async (event: any, message: any) => {
|
||||||
event.returnValue = await this.authenticateBiometric();
|
return await this.authenticateBiometric();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async supportsBiometric(): Promise<boolean> {
|
async supportsBiometric(): Promise<boolean> {
|
||||||
const availability = await this.checkAvailabilityAsync();
|
return Promise.resolve(true);
|
||||||
|
|
||||||
return this.getAllowedAvailabilities().includes(availability);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticateBiometric(): Promise<boolean> {
|
async authenticateBiometric(): Promise<boolean> {
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
const hwnd = this.windowMain.win.getNativeWindowHandle();
|
||||||
if (module == null) {
|
return await biometrics.prompt(hwnd, this.i18nservice.t("windowsHelloConsentMessage"));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Get someone with a w7 to verify this doesn't crash
|
||||||
async checkAvailabilityAsync(): Promise<any> {
|
async checkAvailabilityAsync(): Promise<any> {
|
||||||
const module = this.getWindowsSecurityCredentialsUiModule();
|
return await biometrics.available();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export class DesktopCredentialStorageListener {
|
|||||||
constructor(private serviceName: string, private biometricService: BiometricMain) {}
|
constructor(private serviceName: string, private biometricService: BiometricMain) {}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
ipcMain.on("keytar", async (event: any, message: any) => {
|
ipcMain.handle("keytar", async (event: any, message: any) => {
|
||||||
try {
|
try {
|
||||||
let serviceName = this.serviceName;
|
let serviceName = this.serviceName;
|
||||||
message.keySuffix = "_" + (message.keySuffix ?? "");
|
message.keySuffix = "_" + (message.keySuffix ?? "");
|
||||||
@ -35,9 +35,9 @@ export class DesktopCredentialStorageListener {
|
|||||||
await passwords.deletePassword(serviceName, message.key);
|
await passwords.deletePassword(serviceName, message.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event.returnValue = val;
|
return val;
|
||||||
} catch {
|
} catch {
|
||||||
event.returnValue = null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
1058
src/package-lock.json
generated
1058
src/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,6 @@
|
|||||||
"url": "https://github.com/bitwarden/desktop"
|
"url": "https://github.com/bitwarden/desktop"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/desktop-native": "file:../desktop_native",
|
"@bitwarden/desktop-native": "file:../desktop_native"
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
|
||||||
"forcefocus": "^1.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user