Skip to content

Commit 161fd66

Browse files
committed
Initial windows support
1 parent 779b6a2 commit 161fd66

File tree

3 files changed

+110
-45
lines changed

3 files changed

+110
-45
lines changed

guest-js/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { invoke } from "@tauri-apps/api/core";
77
export enum BiometryType {
88
/** No biometry available */
99
None = 0,
10+
/** Automatic biometry (e.g., Face ID, Touch ID) */
11+
Auto = 1,
1012
/** Apple Touch ID or Android fingerprint authentication */
11-
TouchID = 1,
13+
TouchID = 2,
1214
/** Apple Face ID or Android face authentication */
13-
FaceID = 2,
15+
FaceID = 3,
1416
/** Android iris authentication (Samsung devices) */
15-
Iris = 3,
17+
Iris = 4,
1618
}
1719

1820
/**

src/models.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ pub struct AuthenticatePayload {
2828
#[repr(u8)]
2929
pub enum BiometryType {
3030
None = 0,
31-
TouchID = 1,
32-
FaceID = 2,
31+
Auto = 1,
32+
TouchID = 2,
33+
FaceID = 3,
3334
}
3435

3536
#[derive(Debug, Clone, Deserialize, Serialize)]

src/windows.rs

Lines changed: 102 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,15 @@ use windows::{
77
core::*,
88
Foundation::IAsyncOperation,
99
Security::Credentials::UI::{
10-
UserConsentVerifier, UserConsentVerificationResult, UserConsentVerifierAvailability,
10+
UserConsentVerificationResult, UserConsentVerifier, UserConsentVerifierAvailability,
1111
},
1212
Security::Credentials::{
13-
KeyCredentialManager, KeyCredentialCreationOption, KeyCredentialRetrievalResult,
13+
KeyCredentialCreationOption, KeyCredentialManager, KeyCredentialRetrievalResult,
1414
},
1515
Win32::{
1616
Foundation::HWND,
1717
UI::WindowsAndMessaging::{
18-
FindWindowW, SetForegroundWindow, ShowWindow, BringWindowToTop,
19-
IsIconic, SW_RESTORE,
18+
BringWindowToTop, FindWindowW, IsIconic, SetForegroundWindow, ShowWindow, SW_RESTORE,
2019
},
2120
},
2221
};
@@ -31,7 +30,10 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
3130
#[inline]
3231
fn to_wide(s: &str) -> Vec<u16> {
3332
use std::os::windows::ffi::OsStrExt;
34-
std::ffi::OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect()
33+
std::ffi::OsStr::new(s)
34+
.encode_wide()
35+
.chain(std::iter::once(0))
36+
.collect()
3537
}
3638

3739
/// Try to find and foreground the Windows Hello credential dialog.
@@ -76,75 +78,135 @@ pub struct Biometry<R: Runtime>(AppHandle<R>);
7678
impl<R: Runtime> Biometry<R> {
7779
pub fn status(&self) -> crate::Result<Status> {
7880
let availability = UserConsentVerifier::CheckAvailabilityAsync()
79-
.map_err(|e| crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to check availability: {:?}", e))))?
80-
.get()
81-
.map_err(|e| crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to get availability: {:?}", e))))?;
81+
.and_then(|async_op| async_op.get())
82+
.map_err(|e| {
83+
crate::Error::from(std::io::Error::new(
84+
std::io::ErrorKind::Other,
85+
format!("Failed to check biometry availability: {:?}", e),
86+
))
87+
})?;
8288

83-
Ok(Status {
84-
is_available: matches!(
85-
availability,
86-
UserConsentVerifierAvailability::Available
89+
let (is_available, biometry_type, error, error_code) = match availability {
90+
UserConsentVerifierAvailability::Available => (true, BiometryType::Auto, None, None),
91+
UserConsentVerifierAvailability::DeviceNotPresent => (
92+
false,
93+
BiometryType::None,
94+
Some("No biometric device found".to_string()),
95+
Some("biometryNotAvailable".to_string()),
8796
),
88-
biometry_type: if matches!(
89-
availability,
90-
UserConsentVerifierAvailability::Available
91-
) {
92-
BiometryType::FaceID // Windows Hello supports multiple modalities, but we simplify here
93-
} else {
94-
BiometryType::None
95-
},
96-
error: None,
97-
error_code: None,
97+
UserConsentVerifierAvailability::NotConfiguredForUser => (
98+
false,
99+
BiometryType::None,
100+
Some("Biometric authentication not configured".to_string()),
101+
Some("biometryNotEnrolled".to_string()),
102+
),
103+
UserConsentVerifierAvailability::DisabledByPolicy => (
104+
false,
105+
BiometryType::None,
106+
Some("Biometric authentication disabled by policy".to_string()),
107+
Some("biometryNotAvailable".to_string()),
108+
),
109+
UserConsentVerifierAvailability::DeviceBusy => (
110+
false,
111+
BiometryType::None,
112+
Some("Biometric device is busy".to_string()),
113+
Some("systemCancel".to_string()),
114+
),
115+
_ => (
116+
false,
117+
BiometryType::None,
118+
Some("Unknown availability status".to_string()),
119+
Some("biometryNotAvailable".to_string()),
120+
),
121+
};
122+
123+
Ok(Status {
124+
is_available,
125+
biometry_type,
126+
error,
127+
error_code,
98128
})
99129
}
100130

101-
pub fn authenticate(&self, _reason: String, _options: AuthOptions) -> crate::Result<()> {
131+
pub fn authenticate(&self, reason: String, _options: AuthOptions) -> crate::Result<()> {
102132
let result = UserConsentVerifier::RequestVerificationAsync(&HSTRING::from(reason))
133+
.and_then(|async_op| {
134+
nudge_hello_dialog_focus_async(5, 250);
135+
async_op.get()
136+
})
103137
.map_err(|e| {
104138
crate::Error::from(std::io::Error::new(
105139
std::io::ErrorKind::Other,
106-
format!("Failed to request verification: {:?}", e),
107-
))
108-
})?
109-
.get()
110-
.map_err(|e| {
111-
crate::Error::from(std::io::Error::new(
112-
std::io::ErrorKind::Other,
113-
format!("Failed to get verification result: {:?}", e),
140+
format!("Failed to request user verification: {:?}", e),
114141
))
115142
})?;
116143

117144
match result {
118145
UserConsentVerificationResult::Verified => Ok(()),
119-
UserConsentVerificationResult::DeviceBusy => Err(crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, "Device is busy"))),
120-
UserConsentVerificationResult::DeviceNotPresent => Err(crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, "No biometric device found"))),
121-
UserConsentVerificationResult::DisabledByPolicy => Err(crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, "Biometric authentication is disabled by policy"))),
122-
UserConsentVerificationResult::NotConfiguredForUser => Err(crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, "Biometric authentication is not configured for the user"))),
123-
_ => Err(crate::Error::from(std::io::Error::new(std::io::ErrorKind::Other, "Authentication failed"))),
146+
UserConsentVerificationResult::DeviceBusy => {
147+
Err(crate::Error::from(std::io::Error::new(
148+
std::io::ErrorKind::ResourceBusy,
149+
"Device is busy",
150+
)))
151+
}
152+
UserConsentVerificationResult::DeviceNotPresent => {
153+
Err(crate::Error::from(std::io::Error::new(
154+
std::io::ErrorKind::NotFound,
155+
"No biometric device found",
156+
)))
157+
}
158+
UserConsentVerificationResult::DisabledByPolicy => {
159+
Err(crate::Error::from(std::io::Error::new(
160+
std::io::ErrorKind::PermissionDenied,
161+
"Biometric authentication is disabled by policy",
162+
)))
163+
}
164+
UserConsentVerificationResult::NotConfiguredForUser => {
165+
Err(crate::Error::from(std::io::Error::new(
166+
std::io::ErrorKind::Other,
167+
"Biometric authentication is not configured for the user",
168+
)))
169+
}
170+
UserConsentVerificationResult::Canceled => {
171+
Err(crate::Error::from(std::io::Error::new(
172+
std::io::ErrorKind::Interrupted,
173+
"Authentication was canceled by the user",
174+
)))
175+
}
176+
UserConsentVerificationResult::RetriesExhausted => {
177+
Err(crate::Error::from(std::io::Error::new(
178+
std::io::ErrorKind::PermissionDenied,
179+
"Too many failed authentication attempts",
180+
)))
181+
}
182+
_ => Err(crate::Error::from(std::io::Error::new(
183+
std::io::ErrorKind::Other,
184+
"Authentication failed",
185+
))),
124186
}
125187
}
126188

127189
pub fn has_data(&self, _options: DataOptions) -> crate::Result<bool> {
128190
Err(crate::Error::from(std::io::Error::other(
129-
"Biometry is not supported on desktop platforms",
191+
"Has data is not supported on windows platform",
130192
)))
131193
}
132194

133195
pub fn get_data(&self, _options: GetDataOptions) -> crate::Result<DataResponse> {
134196
Err(crate::Error::from(std::io::Error::other(
135-
"Biometry is not supported on desktop platforms",
197+
"Get data is not supported on windows platform",
136198
)))
137199
}
138200

139201
pub fn set_data(&self, _options: SetDataOptions) -> crate::Result<()> {
140202
Err(crate::Error::from(std::io::Error::other(
141-
"Biometry is not supported on desktop platforms",
203+
"Set data is not supported on windows platform",
142204
)))
143205
}
144206

145207
pub fn remove_data(&self, _options: RemoveDataOptions) -> crate::Result<()> {
146208
Err(crate::Error::from(std::io::Error::other(
147-
"Biometry is not supported on desktop platforms",
209+
"Remove data is not supported on windows platform",
148210
)))
149211
}
150212
}

0 commit comments

Comments
 (0)