Skip to content

Commit

Permalink
[PoC] Add biometrics authentication method
Browse files Browse the repository at this point in the history
  • Loading branch information
vxgmichel committed Feb 12, 2025
1 parent 2e7bf35 commit 2dc9861
Show file tree
Hide file tree
Showing 19 changed files with 533 additions and 6 deletions.
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

0 comments on commit 2dc9861

Please sign in to comment.