Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PoC] Add biometrics authentication method #9681

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions bindings/electron/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export enum CancelledGreetingAttemptReason {
}

export enum DeviceFileType {
Biometrics = 'DeviceFileTypeBiometrics',
Keyring = 'DeviceFileTypeKeyring',
Password = 'DeviceFileTypePassword',
Recovery = 'DeviceFileTypeRecovery',
Expand Down Expand Up @@ -1562,6 +1563,10 @@ export type ClientUserUpdateProfileError =


// DeviceAccessStrategy
export interface DeviceAccessStrategyBiometrics {
tag: "Biometrics"
key_file: string
}
export interface DeviceAccessStrategyKeyring {
tag: "Keyring"
key_file: string
Expand All @@ -1576,12 +1581,16 @@ export interface DeviceAccessStrategySmartcard {
key_file: string
}
export type DeviceAccessStrategy =
| DeviceAccessStrategyBiometrics
| DeviceAccessStrategyKeyring
| DeviceAccessStrategyPassword
| DeviceAccessStrategySmartcard


// DeviceSaveStrategy
export interface DeviceSaveStrategyBiometrics {
tag: "Biometrics"
}
export interface DeviceSaveStrategyKeyring {
tag: "Keyring"
}
Expand All @@ -1593,6 +1602,7 @@ export interface DeviceSaveStrategySmartcard {
tag: "Smartcard"
}
export type DeviceSaveStrategy =
| DeviceSaveStrategyBiometrics
| DeviceSaveStrategyKeyring
| DeviceSaveStrategyPassword
| DeviceSaveStrategySmartcard
Expand Down Expand Up @@ -3890,6 +3900,8 @@ export function importRecoveryDevice(
export function initLibparsec(
config: ClientConfig
): Promise<null>
export function isBiometricsAvailable(
): Promise<boolean>
export function isKeyringAvailable(
): Promise<boolean>
export function listAvailableDevices(
Expand Down
49 changes: 49 additions & 0 deletions bindings/electron/src/meths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ fn enum_device_file_type_js_to_rs<'a>(
raw_value: &str,
) -> NeonResult<libparsec::DeviceFileType> {
match raw_value {
"DeviceFileTypeBiometrics" => Ok(libparsec::DeviceFileType::Biometrics),
"DeviceFileTypeKeyring" => Ok(libparsec::DeviceFileType::Keyring),
"DeviceFileTypePassword" => Ok(libparsec::DeviceFileType::Password),
"DeviceFileTypeRecovery" => Ok(libparsec::DeviceFileType::Recovery),
Expand All @@ -92,6 +93,7 @@ fn enum_device_file_type_js_to_rs<'a>(
#[allow(dead_code)]
fn enum_device_file_type_rs_to_js(value: libparsec::DeviceFileType) -> &'static str {
match value {
libparsec::DeviceFileType::Biometrics => "DeviceFileTypeBiometrics",
libparsec::DeviceFileType::Keyring => "DeviceFileTypeKeyring",
libparsec::DeviceFileType::Password => "DeviceFileTypePassword",
libparsec::DeviceFileType::Recovery => "DeviceFileTypeRecovery",
Expand Down Expand Up @@ -6629,6 +6631,20 @@ fn variant_device_access_strategy_js_to_rs<'a>(
) -> NeonResult<libparsec::DeviceAccessStrategy> {
let tag = obj.get::<JsString, _, _>(cx, "tag")?.value(cx);
match tag.as_str() {
"DeviceAccessStrategyBiometrics" => {
let key_file = {
let js_val: Handle<JsString> = obj.get(cx, "keyFile")?;
{
let custom_from_rs_string =
|s: String| -> Result<_, &'static str> { Ok(std::path::PathBuf::from(s)) };
match custom_from_rs_string(js_val.value(cx)) {
Ok(val) => val,
Err(err) => return cx.throw_type_error(err),
}
}
};
Ok(libparsec::DeviceAccessStrategy::Biometrics { key_file })
}
"DeviceAccessStrategyKeyring" => {
let key_file = {
let js_val: Handle<JsString> = obj.get(cx, "keyFile")?;
Expand Down Expand Up @@ -6692,6 +6708,23 @@ fn variant_device_access_strategy_rs_to_js<'a>(
) -> NeonResult<Handle<'a, JsObject>> {
let js_obj = cx.empty_object();
match rs_obj {
libparsec::DeviceAccessStrategy::Biometrics { key_file, .. } => {
let js_tag = JsString::try_new(cx, "DeviceAccessStrategyBiometrics").or_throw(cx)?;
js_obj.set(cx, "tag", js_tag)?;
let js_key_file = JsString::try_new(cx, {
let custom_to_rs_string = |path: std::path::PathBuf| -> Result<_, _> {
path.into_os_string()
.into_string()
.map_err(|_| "Path contains non-utf8 characters")
};
match custom_to_rs_string(key_file) {
Ok(ok) => ok,
Err(err) => return cx.throw_type_error(err),
}
})
.or_throw(cx)?;
js_obj.set(cx, "keyFile", js_key_file)?;
}
libparsec::DeviceAccessStrategy::Keyring { key_file, .. } => {
let js_tag = JsString::try_new(cx, "DeviceAccessStrategyKeyring").or_throw(cx)?;
js_obj.set(cx, "tag", js_tag)?;
Expand Down Expand Up @@ -6760,6 +6793,7 @@ fn variant_device_save_strategy_js_to_rs<'a>(
) -> NeonResult<libparsec::DeviceSaveStrategy> {
let tag = obj.get::<JsString, _, _>(cx, "tag")?.value(cx);
match tag.as_str() {
"DeviceSaveStrategyBiometrics" => Ok(libparsec::DeviceSaveStrategy::Biometrics {}),
"DeviceSaveStrategyKeyring" => Ok(libparsec::DeviceSaveStrategy::Keyring {}),
"DeviceSaveStrategyPassword" => {
let password = {
Expand All @@ -6786,6 +6820,10 @@ fn variant_device_save_strategy_rs_to_js<'a>(
) -> NeonResult<Handle<'a, JsObject>> {
let js_obj = cx.empty_object();
match rs_obj {
libparsec::DeviceSaveStrategy::Biometrics { .. } => {
let js_tag = JsString::try_new(cx, "DeviceSaveStrategyBiometrics").or_throw(cx)?;
js_obj.set(cx, "tag", js_tag)?;
}
libparsec::DeviceSaveStrategy::Keyring { .. } => {
let js_tag = JsString::try_new(cx, "DeviceSaveStrategyKeyring").or_throw(cx)?;
js_obj.set(cx, "tag", js_tag)?;
Expand Down Expand Up @@ -18981,6 +19019,16 @@ fn init_libparsec(mut cx: FunctionContext) -> JsResult<JsPromise> {
Ok(promise)
}

// is_biometrics_available
fn is_biometrics_available(mut cx: FunctionContext) -> JsResult<JsPromise> {
crate::init_sentry();
let ret = libparsec::is_biometrics_available();
let js_ret = JsBoolean::new(&mut cx, ret);
let (deferred, promise) = cx.promise();
deferred.resolve(&mut cx, js_ret);
Ok(promise)
}

// is_keyring_available
fn is_keyring_available(mut cx: FunctionContext) -> JsResult<JsPromise> {
crate::init_sentry();
Expand Down Expand Up @@ -24049,6 +24097,7 @@ pub fn register_meths(cx: &mut ModuleContext) -> NeonResult<()> {
)?;
cx.export_function("importRecoveryDevice", import_recovery_device)?;
cx.export_function("initLibparsec", init_libparsec)?;
cx.export_function("isBiometricsAvailable", is_biometrics_available)?;
cx.export_function("isKeyringAvailable", is_keyring_available)?;
cx.export_function("listAvailableDevices", list_available_devices)?;
cx.export_function("mountpointToOsPath", mountpoint_to_os_path)?;
Expand Down
7 changes: 7 additions & 0 deletions bindings/generator/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class Password:
class Smartcard:
key_file: Path

class Biometrics:
key_file: Path


class ClientStartError(ErrorVariant):
class DeviceUsedByAnotherProcess:
Expand Down Expand Up @@ -459,6 +462,10 @@ def is_keyring_available() -> bool:
raise NotImplementedError


def is_biometrics_available() -> bool:
raise NotImplementedError


class ImportRecoveryDeviceError(ErrorVariant):
class Internal:
pass
Expand Down
4 changes: 4 additions & 0 deletions bindings/generator/api/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DeviceFileType(Enum):
Password = EnumItemUnit
Recovery = EnumItemUnit
Smartcard = EnumItemUnit
Biometrics = EnumItemUnit


class DeviceSaveStrategy(Variant):
Expand All @@ -36,6 +37,9 @@ class Password:
class Smartcard:
pass

class Biometrics:
pass


class AvailableDevice(Structure):
key_file_path: Path
Expand Down
58 changes: 58 additions & 0 deletions bindings/web/src/meths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fn enum_cancelled_greeting_attempt_reason_rs_to_js(
#[allow(dead_code)]
fn enum_device_file_type_js_to_rs(raw_value: &str) -> Result<libparsec::DeviceFileType, JsValue> {
match raw_value {
"DeviceFileTypeBiometrics" => Ok(libparsec::DeviceFileType::Biometrics),
"DeviceFileTypeKeyring" => Ok(libparsec::DeviceFileType::Keyring),
"DeviceFileTypePassword" => Ok(libparsec::DeviceFileType::Password),
"DeviceFileTypeRecovery" => Ok(libparsec::DeviceFileType::Recovery),
Expand All @@ -98,6 +99,7 @@ fn enum_device_file_type_js_to_rs(raw_value: &str) -> Result<libparsec::DeviceFi
#[allow(dead_code)]
fn enum_device_file_type_rs_to_js(value: libparsec::DeviceFileType) -> &'static str {
match value {
libparsec::DeviceFileType::Biometrics => "DeviceFileTypeBiometrics",
libparsec::DeviceFileType::Keyring => "DeviceFileTypeKeyring",
libparsec::DeviceFileType::Password => "DeviceFileTypePassword",
libparsec::DeviceFileType::Recovery => "DeviceFileTypeRecovery",
Expand Down Expand Up @@ -7322,6 +7324,24 @@ fn variant_device_access_strategy_js_to_rs(
.as_string()
.ok_or_else(|| JsValue::from(TypeError::new("tag isn't a string")))?;
match tag.as_str() {
"DeviceAccessStrategyBiometrics" => {
let key_file = {
let js_val = Reflect::get(&obj, &"keyFile".into())?;
js_val
.dyn_into::<JsString>()
.ok()
.and_then(|s| s.as_string())
.ok_or_else(|| TypeError::new("Not a string"))
.and_then(|x| {
let custom_from_rs_string = |s: String| -> Result<_, &'static str> {
Ok(std::path::PathBuf::from(s))
};
custom_from_rs_string(x).map_err(|e| TypeError::new(e.as_ref()))
})
.map_err(|_| TypeError::new("Not a valid Path"))?
};
Ok(libparsec::DeviceAccessStrategy::Biometrics { key_file })
}
"DeviceAccessStrategyKeyring" => {
let key_file = {
let js_val = Reflect::get(&obj, &"keyFile".into())?;
Expand Down Expand Up @@ -7402,6 +7422,26 @@ fn variant_device_access_strategy_rs_to_js(
) -> Result<JsValue, JsValue> {
let js_obj = Object::new().into();
match rs_obj {
libparsec::DeviceAccessStrategy::Biometrics { key_file, .. } => {
Reflect::set(
&js_obj,
&"tag".into(),
&"DeviceAccessStrategyBiometrics".into(),
)?;
let js_key_file = JsValue::from_str({
let custom_to_rs_string = |path: std::path::PathBuf| -> Result<_, _> {
path.into_os_string()
.into_string()
.map_err(|_| "Path contains non-utf8 characters")
};
match custom_to_rs_string(key_file) {
Ok(ok) => ok,
Err(err) => return Err(JsValue::from(TypeError::new(err.as_ref()))),
}
.as_ref()
});
Reflect::set(&js_obj, &"keyFile".into(), &js_key_file)?;
}
libparsec::DeviceAccessStrategy::Keyring { key_file, .. } => {
Reflect::set(
&js_obj,
Expand Down Expand Up @@ -7481,6 +7521,7 @@ fn variant_device_save_strategy_js_to_rs(
.as_string()
.ok_or_else(|| JsValue::from(TypeError::new("tag isn't a string")))?;
match tag.as_str() {
"DeviceSaveStrategyBiometrics" => Ok(libparsec::DeviceSaveStrategy::Biometrics {}),
"DeviceSaveStrategyKeyring" => Ok(libparsec::DeviceSaveStrategy::Keyring {}),
"DeviceSaveStrategyPassword" => {
let password = {
Expand Down Expand Up @@ -7512,6 +7553,13 @@ fn variant_device_save_strategy_rs_to_js(
) -> Result<JsValue, JsValue> {
let js_obj = Object::new().into();
match rs_obj {
libparsec::DeviceSaveStrategy::Biometrics { .. } => {
Reflect::set(
&js_obj,
&"tag".into(),
&"DeviceSaveStrategyBiometrics".into(),
)?;
}
libparsec::DeviceSaveStrategy::Keyring { .. } => {
Reflect::set(&js_obj, &"tag".into(), &"DeviceSaveStrategyKeyring".into())?;
}
Expand Down Expand Up @@ -17947,6 +17995,16 @@ pub fn initLibparsec(config: Object) -> Promise {
})
}

// is_biometrics_available
#[allow(non_snake_case)]
#[wasm_bindgen]
pub fn isBiometricsAvailable() -> Promise {
future_to_promise(async move {
let ret = libparsec::is_biometrics_available();
Ok(ret.into())
})
}

// is_keyring_available
#[allow(non_snake_case)]
#[wasm_bindgen]
Expand Down
Loading
Loading