diff --git a/credentialsd-common/src/model.rs b/credentialsd-common/src/model.rs index 758d87da..f673ca12 100644 --- a/credentialsd-common/src/model.rs +++ b/credentialsd-common/src/model.rs @@ -274,6 +274,8 @@ pub enum Error { AuthenticatorError, /// No matching credentials were found on the device. NoCredentials, + /// Credential was already registered with this device (credential ID contained in excludeCredentials) + CredentialExcluded, /// Too many incorrect PIN attempts, and authenticator must be removed and /// reinserted to continue any more PIN attempts. /// @@ -292,6 +294,7 @@ impl Display for Error { match self { Self::AuthenticatorError => f.write_str("AuthenticatorError"), Self::NoCredentials => f.write_str("NoCredentials"), + Self::CredentialExcluded => f.write_str("CredentialExcluded"), Self::PinAttemptsExhausted => f.write_str("PinAttemptsExhausted"), Self::Internal(s) => write!(f, "InternalError: {s}"), } diff --git a/credentialsd-common/src/server.rs b/credentialsd-common/src/server.rs index daa87431..555f4aa0 100644 --- a/credentialsd-common/src/server.rs +++ b/credentialsd-common/src/server.rs @@ -336,6 +336,9 @@ pub enum ServiceError { /// No matching credentials were found on the device. NoCredentials, + /// Credential was already registered with this device (credential ID contained in excludeCredentials) + CredentialExcluded, + /// Too many incorrect PIN attempts, and authenticator must be removed and /// reinserted to continue any more PIN attempts. /// @@ -363,6 +366,7 @@ impl From for crate::model::Error { match value { ServiceError::AuthenticatorError => Self::AuthenticatorError, ServiceError::NoCredentials => Self::NoCredentials, + ServiceError::CredentialExcluded => Self::CredentialExcluded, ServiceError::PinAttemptsExhausted => Self::PinAttemptsExhausted, // TODO: this is bogus, we should refactor to remove the tuple field // and let the client decide how to render the error. @@ -439,6 +443,7 @@ impl TryFrom for crate::model::UsbState { match error_code.as_ref() { "AuthenticatorError" => ServiceError::AuthenticatorError, "NoCredentials" => ServiceError::NoCredentials, + "CredentialExcluded" => ServiceError::CredentialExcluded, "PinAttemptsExhausted" => ServiceError::PinAttemptsExhausted, _ => ServiceError::Internal, } diff --git a/credentialsd-ui/src/gui/view_model/mod.rs b/credentialsd-ui/src/gui/view_model/mod.rs index b9be3dd9..cf139743 100644 --- a/credentialsd-ui/src/gui/view_model/mod.rs +++ b/credentialsd-ui/src/gui/view_model/mod.rs @@ -230,6 +230,9 @@ impl ViewModel { Error::AuthenticatorError | Error::Internal(_) => { "Something went wrong while retrieving a credential. Please try again later or use a different authenticator." } + Error::CredentialExcluded => { + "This credential is already registered on this authenticator." + } }); self.tx_update .send(ViewUpdate::Failed(error_msg)) diff --git a/credentialsd/src/credential_service/usb.rs b/credentialsd/src/credential_service/usb.rs index fab50ccf..b32a2a4c 100644 --- a/credentialsd/src/credential_service/usb.rs +++ b/credentialsd/src/credential_service/usb.rs @@ -343,6 +343,7 @@ async fn handle_events( .map_err(|err| match err { WebAuthnError::Ctap(CtapError::PINAuthBlocked) => Error::PinAttemptsExhausted, WebAuthnError::Ctap(CtapError::NoCredentials) => Error::NoCredentials, + WebAuthnError::Ctap(CtapError::CredentialExcluded) => Error::CredentialExcluded, _ => Error::AuthenticatorError, }); if let Err(err) = signal_tx.send(response).await {