Skip to content

Commit 51635f0

Browse files
committed
Implement blinking cancellation if multiple devices are found
1 parent a01671c commit 51635f0

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

xyz-iinuwa-credential-manager-portal-gtk/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ serde_json = "1.0.140"
2020
tracing = "0.1.41"
2121
tracing-subscriber = "0.3"
2222
zbus = { version = "5.5.0", default-features = false, features = ["blocking-api", "tokio"] }
23-
libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn", rev = "c61492dcc66cc53b33e9d3eb3377017019332964" }
23+
libwebauthn = { git = "https://github.com/linux-credentials/libwebauthn", rev = "21995110e729cb83f3cd5ff3ece4c42315fe8bd3" }
2424
async-trait = "0.1.88"
2525
tokio = { version = "1.45.0", features = ["rt-multi-thread"] }
2626
futures-lite = "2.6.0"

xyz-iinuwa-credential-manager-portal-gtk/src/credential_service/usb.rs

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
use std::time::Duration;
1+
use std::{collections::HashMap, time::Duration};
22

33
use async_stream::stream;
44
use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine};
55
use futures_lite::Stream;
66
use libwebauthn::{
77
ops::webauthn::GetAssertionResponse,
88
proto::CtapError,
9-
transport::{hid::HidDevice, Device},
9+
transport::{
10+
hid::{channel::HidChannelHandle, HidDevice},
11+
Device,
12+
},
1013
webauthn::{Error as WebAuthnError, WebAuthn},
1114
UxUpdate,
1215
};
@@ -71,59 +74,76 @@ impl InProcessUsbHandler {
7174
}
7275
}
7376
UsbStateInternal::SelectingDevice(hid_devices) => {
77+
let expected_answers = hid_devices.len();
7478
let (blinking_tx, mut blinking_rx) =
75-
tokio::sync::mpsc::channel::<Option<HidDevice>>(hid_devices.len());
76-
let mut expected_answers = hid_devices.len();
77-
for mut device in hid_devices {
79+
tokio::sync::mpsc::channel::<Option<usize>>(expected_answers);
80+
let mut channel_map = HashMap::new();
81+
let (setup_tx, mut setup_rx) =
82+
tokio::sync::mpsc::channel::<(usize, HidDevice, HidChannelHandle)>(
83+
expected_answers,
84+
);
85+
for (idx, mut device) in hid_devices.into_iter().enumerate() {
86+
let stx = setup_tx.clone();
7887
let tx = blinking_tx.clone();
7988
tokio::spawn(async move {
89+
let dev = device.clone();
90+
8091
let res = match device.channel().await {
81-
Ok((ref mut channel, _)) => channel
82-
.blink_and_wait_for_user_presence(Duration::from_secs(300))
83-
.await
84-
.map_err(|err| {
85-
format!(
92+
Ok((ref mut channel, _)) => {
93+
let cancel_handle = channel.get_handle();
94+
stx.send((idx, dev, cancel_handle)).await.unwrap();
95+
drop(stx);
96+
97+
let was_selected = channel
98+
.blink_and_wait_for_user_presence(Duration::from_secs(300))
99+
.await;
100+
match was_selected {
101+
Ok(true) => Ok(Some(idx)),
102+
Ok(false) => Ok(None),
103+
Err(err) => Err(format!(
86104
"Failed to send wink request to authenticator: {:?}",
87105
err
88-
)
89-
})
90-
.and_then(|blinking| {
91-
if blinking {
92-
Ok(())
93-
} else {
94-
Err("Authenticator was not able to blink".to_string())
95-
}
96-
}),
106+
)),
107+
}
108+
}
97109
Err(err) => Err(format!(
98110
"Failed to create channel for USB authenticator: {:?}",
99111
err
100112
)),
101113
}
102114
.inspect_err(|err| tracing::warn!(err))
103-
.ok();
104-
105-
if let Err(err) = tx.send(res.map(|_| device)).await {
115+
.unwrap_or_default(); // In case of error, we also send `None`
116+
if let Err(err) = tx.send(res).await {
106117
tracing::error!(
107118
"Failed to send notification of wink response: {:?}",
108119
err,
109120
);
110121
}
111122
});
112123
}
124+
drop(setup_tx);
125+
// Receiving all cancel handles
126+
while let Some((idx, device, handle)) = setup_rx.recv().await {
127+
channel_map.insert(idx, (device, handle));
128+
}
129+
130+
tracing::info!("Waiting for user interaction");
131+
drop(blinking_tx);
113132
let mut state = UsbStateInternal::Idle;
114133
while let Some(msg) = blinking_rx.recv().await {
115-
expected_answers -= 1;
116134
match msg {
117-
Some(device) => {
135+
Some(idx) => {
136+
let (device, _handle) = channel_map.remove(&idx).unwrap();
137+
tracing::info!("User selected device {device:?}.");
138+
for (_key, (device, handle)) in channel_map.into_iter() {
139+
tracing::info!("Cancelling device {device:?}.");
140+
handle.cancel_ongoing_operation().await;
141+
}
118142
state = UsbStateInternal::Connected(device);
119143
break;
120144
}
121145
None => {
122-
if expected_answers == 0 {
123-
break;
124-
} else {
125-
continue;
126-
}
146+
continue;
127147
}
128148
}
129149
}

0 commit comments

Comments
 (0)