Skip to content

Commit 7ca3efe

Browse files
committed
Push errors up out of background tasks.
1 parent a457b57 commit 7ca3efe

File tree

2 files changed

+69
-93
lines changed
  • xyz-iinuwa-credential-manager-portal-gtk/src/credential_service

2 files changed

+69
-93
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ enum AuthenticatorResponse {
197197
#[derive(Debug, Clone)]
198198
pub enum Error {
199199
AuthenticatorError,
200+
NoCredentials,
201+
PinAttemptsExhausted,
202+
UserVerficiationAttemptsExhausted,
203+
Internal(String),
200204
}
201205

202206
impl From<MakeCredentialResponse> for AuthenticatorResponse {

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

Lines changed: 65 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine};
55
use futures_lite::Stream;
66
use libwebauthn::{
77
ops::webauthn::GetAssertionResponse,
8+
proto::CtapError,
89
transport::{hid::HidDevice, Device},
910
webauthn::{Error as WebAuthnError, WebAuthn},
1011
UxUpdate,
@@ -17,7 +18,7 @@ use crate::{
1718
view_model::Credential,
1819
};
1920

20-
use super::{AuthenticatorResponse, CredentialResponse};
21+
use super::{AuthenticatorResponse, CredentialResponse, Error};
2122

2223
pub(crate) trait UsbHandler {
2324
fn start(
@@ -33,7 +34,7 @@ impl InProcessUsbHandler {
3334
async fn process(
3435
tx: Sender<UsbStateInternal>,
3536
cred_request: CredentialRequest,
36-
) -> Result<(), String> {
37+
) -> Result<(), Error> {
3738
let mut state = UsbStateInternal::Idle;
3839
let (signal_tx, mut signal_rx) = mpsc::channel(256);
3940
let (cred_tx, mut cred_rx) = mpsc::channel(1);
@@ -58,7 +59,7 @@ impl InProcessUsbHandler {
5859
Err(err) => {
5960
failures += 1;
6061
if failures == 5 {
61-
Err(format!("Failed to list USB authenticators: {:?}. Cancelling USB state updates.", err))
62+
Err(Error::Internal(format!("Failed to list USB authenticators: {:?}. Cancelling USB state updates.", err)))
6263
} else {
6364
tracing::warn!("Failed to list USB authenticators: {:?}. Throttling USB state updates", err);
6465
tokio::time::sleep(Duration::from_secs(1)).await;
@@ -173,18 +174,16 @@ impl InProcessUsbHandler {
173174
}
174175
},
175176
Some(Err(err)) => Err(err.clone()),
176-
None => Err("Channel disconnected".to_string()),
177+
None => Err(Error::Internal("Channel disconnected".to_string())),
177178
}
178179
}
179180
UsbStateInternal::NeedsPin {
180181
attempts_left: Some(attempts_left),
181182
..
182-
} if attempts_left <= 1 => Err("No more USB attempts left".to_string()),
183+
} if attempts_left <= 1 => Err(Error::PinAttemptsExhausted),
183184
UsbStateInternal::NeedsUserVerification {
184185
attempts_left: Some(attempts_left),
185-
} if attempts_left <= 1 => {
186-
Err("No more on-device user device attempts left".to_string())
187-
}
186+
} if attempts_left <= 1 => Err(Error::UserVerficiationAttemptsExhausted),
188187
UsbStateInternal::NeedsPin { .. }
189188
| UsbStateInternal::NeedsUserVerification { .. }
190189
| UsbStateInternal::NeedsUserPresence => match signal_rx.recv().await {
@@ -227,7 +226,7 @@ impl InProcessUsbHandler {
227226
}
228227
},
229228
},
230-
None => Err("USB UV handler channel closed".to_string()),
229+
None => Err(Error::Internal("USB UV handler channel closed".to_string())),
231230
},
232231
UsbStateInternal::Completed(_) => Ok(prev_usb_state),
233232
UsbStateInternal::SelectCredential {
@@ -262,19 +261,21 @@ impl InProcessUsbHandler {
262261
),
263262
),
264263
)),
265-
None => Err("Selected credential not found.".to_string()),
264+
None => Err(Error::NoCredentials),
266265
}
267266
}
268267
None => {
269268
tracing::debug!("cred channel closed before receiving cred from client.");
270-
Err("Cred channel disconnected".to_string())
269+
Err(Error::Internal(
270+
"Cred channel disconnected prematurely".to_string(),
271+
))
271272
}
272273
},
273274
};
274275
state = next_usb_state?;
275-
tx.send(state.clone())
276-
.await
277-
.map_err(|_| "Receiver channel closed".to_string())?;
276+
tx.send(state.clone()).await.map_err(|_| {
277+
Error::Internal("USB state channel receiver closed prematurely".to_string())
278+
})?;
278279
if let UsbStateInternal::Completed(_) = state {
279280
break Ok(());
280281
}
@@ -285,7 +286,7 @@ impl InProcessUsbHandler {
285286
async fn handle_events(
286287
cred_request: &CredentialRequest,
287288
mut device: HidDevice,
288-
signal_tx: &Sender<Result<UsbUvMessage, String>>,
289+
signal_tx: &Sender<Result<UsbUvMessage, Error>>,
289290
) {
290291
let device_debug = device.to_string();
291292
match device.channel().await {
@@ -298,86 +299,54 @@ async fn handle_events(
298299
handle_usb_updates(&signal_tx2, state_rx).await;
299300
debug!("Reached end of USB update task");
300301
});
301-
match cred_request {
302-
CredentialRequest::CreatePublicKeyCredentialRequest(make_cred_request) => loop {
303-
tracing::debug!(
304-
"Polling for credential from USB authenticator {}",
305-
&device_debug
306-
);
307-
match channel.webauthn_make_credential(make_cred_request).await {
308-
Ok(response) => {
309-
tracing::debug!("Received attestation from USB authenticator");
310-
notify_ceremony_completed(
311-
signal_tx,
312-
AuthenticatorResponse::CredentialCreated(response),
313-
)
314-
.await;
315-
break;
316-
}
317-
Err(WebAuthnError::Ctap(ctap_error))
318-
if ctap_error.is_retryable_user_error() =>
319-
{
320-
warn!("Retrying WebAuthn make credential operation");
321-
continue;
322-
}
323-
Err(err) => {
324-
tracing::warn!(
325-
"Failed to create credential with USB authenticator: {:?}",
326-
err
327-
);
328-
notify_ceremony_failed(signal_tx, err.to_string()).await;
329-
break;
330-
}
331-
};
332-
},
333-
CredentialRequest::GetPublicKeyCredentialRequest(get_cred_request) => loop {
334-
match channel.webauthn_get_assertion(get_cred_request).await {
335-
Ok(response) => {
336-
tracing::debug!("Received assertion from USB authenticator");
337-
notify_ceremony_completed(
338-
signal_tx,
339-
AuthenticatorResponse::CredentialsAsserted(response),
340-
)
341-
.await;
342-
break;
343-
}
344-
Err(WebAuthnError::Ctap(ctap_error))
345-
if ctap_error.is_retryable_user_error() =>
346-
{
347-
tracing::warn!("Retrying WebAuthn get credential operation");
348-
continue;
349-
}
350-
Err(err) => {
351-
tracing::warn!(
352-
"Failed to get credential from USB authenticator: {:?}",
353-
err
354-
);
355-
notify_ceremony_failed(signal_tx, err.to_string()).await;
356-
break;
357-
}
358-
};
359-
},
360-
};
302+
tracing::debug!(
303+
"Polling for credential from USB authenticator {}",
304+
&device_debug
305+
);
306+
let response: Result<UsbUvMessage, Error> = loop {
307+
let response = match cred_request {
308+
CredentialRequest::CreatePublicKeyCredentialRequest(make_cred_request) => {
309+
channel
310+
.webauthn_make_credential(make_cred_request)
311+
.await
312+
.map(|response| UsbUvMessage::ReceivedCredentials(response.into()))
313+
}
314+
CredentialRequest::GetPublicKeyCredentialRequest(get_cred_request) => channel
315+
.webauthn_get_assertion(get_cred_request)
316+
.await
317+
.map(|response| UsbUvMessage::ReceivedCredentials(response.into())),
318+
};
319+
match response {
320+
Ok(response) => {
321+
tracing::debug!("Received credential from USB authenticator");
322+
break Ok(response);
323+
}
324+
Err(WebAuthnError::Ctap(ctap_error))
325+
if ctap_error.is_retryable_user_error() =>
326+
{
327+
warn!("Retrying WebAuthn credential operation");
328+
continue;
329+
}
330+
Err(err) => {
331+
tracing::warn!(
332+
"Failed to make/get credential with USB authenticator: {:?}",
333+
err
334+
);
335+
break Err(err);
336+
}
337+
}
338+
}
339+
.map_err(|err| match err {
340+
WebAuthnError::Ctap(CtapError::NoCredentials) => Error::NoCredentials,
341+
_ => Error::AuthenticatorError,
342+
});
343+
if let Err(err) = signal_tx.send(response).await {
344+
tracing::error!("Failed to notify that ceremony completed: {:?}", err);
345+
}
361346
}
362347
}
363348
}
364349

365-
async fn notify_ceremony_completed(
366-
signal_tx: &Sender<Result<UsbUvMessage, String>>,
367-
response: AuthenticatorResponse,
368-
) {
369-
signal_tx
370-
.send(Ok(UsbUvMessage::ReceivedCredentials(response)))
371-
.await
372-
.unwrap();
373-
}
374-
375-
async fn notify_ceremony_failed(signal_tx: &Sender<Result<UsbUvMessage, String>>, err: String) {
376-
if let Err(tx_err) = signal_tx.send(Err(err)).await {
377-
tracing::error!("Failed to notify that ceremony failed: {:?}", tx_err);
378-
}
379-
}
380-
381350
impl UsbHandler for InProcessUsbHandler {
382351
fn start(
383352
&self,
@@ -386,6 +355,8 @@ impl UsbHandler for InProcessUsbHandler {
386355
let request = request.clone();
387356
let (tx, mut rx) = mpsc::channel(32);
388357
tokio::spawn(async move {
358+
// TODO: instead of logging error here, push the errors into the
359+
// stream so credential service can handle/forward them to the UI
389360
if let Err(err) = InProcessUsbHandler::process(tx, request).await {
390361
tracing::error!("Error getting credential from USB: {:?}", err);
391362
}
@@ -550,7 +521,7 @@ impl From<UsbStateInternal> for UsbState {
550521
}
551522

552523
async fn handle_usb_updates(
553-
signal_tx: &WeakSender<Result<UsbUvMessage, String>>,
524+
signal_tx: &WeakSender<Result<UsbUvMessage, Error>>,
554525
mut state_rx: Receiver<UxUpdate>,
555526
) {
556527
while let Some(msg) = state_rx.recv().await {
@@ -570,7 +541,8 @@ async fn handle_usb_updates(
570541
UxUpdate::PinRequired(pin_update) => {
571542
if pin_update.attempts_left.is_some_and(|num| num <= 1) {
572543
// TODO: cancel authenticator operation
573-
if let Err(err) = signal_tx.send(Err("No more PIN attempts allowed. Select a different authenticator or try again later.".to_string())).await {
544+
// Err("No more PIN attempts allowed. Select a different authenticator or try again later.".to_string())
545+
if let Err(err) = signal_tx.send(Err(Error::PinAttemptsExhausted)).await {
574546
tracing::error!("Authenticator cannot process anymore PIN requests, but we cannot relay the message to credential service: {:?}", err);
575547
}
576548
continue;

0 commit comments

Comments
 (0)