Skip to main content

webauthn_kit/
error.rs

1//! Error type for all `webauthn-kit` operations.
2
3/// Error type for `WebAuthn` operations.
4///
5/// The type is `#[non_exhaustive]`: matching on it requires a wildcard arm.
6///
7/// # Security note
8///
9/// Error strings may echo attacker-controlled fragments of the request being
10/// verified (e.g. a base64 payload or an origin string). They are intended for
11/// server-side logs and debugging. Do **not** forward raw error messages to
12/// untrusted clients; surface only a generic verification-failed response.
13#[non_exhaustive]
14#[derive(Debug, thiserror::Error)]
15pub enum WebauthnError {
16    /// A caller-supplied configuration value is invalid or inconsistent.
17    #[error("Configuration error: {0}")]
18    Config(String),
19    /// The presented challenge is unknown, malformed, or was already consumed
20    /// (single-use replay protection triggered).
21    #[error("Invalid challenge: {0}")]
22    InvalidChallenge(String),
23    /// The referenced credential does not exist for the given user/scope.
24    #[error("Credential not found: {0}")]
25    CredentialNotFound(String),
26    /// Generic verification failure (challenge mismatch, bad origin, wrong
27    /// `type` field, truncated authenticator data, RP ID hash mismatch, ...).
28    #[error("Verification failed: {0}")]
29    VerificationFailed(String),
30    /// The registration presented a credential ID that is already registered.
31    #[error("Duplicate credential: {0}")]
32    DuplicateCredential(String),
33    /// The referenced user does not exist.
34    #[error("User not found: {0}")]
35    UserNotFound(String),
36    /// The challenge was consumed after its freshness window elapsed.
37    #[error("Challenge expired")]
38    ChallengeExpired,
39    /// The COSE algorithm is not one of the supported/allowed algorithms.
40    #[error("Unsupported algorithm: {0}")]
41    UnsupportedAlgorithm(i32),
42    /// `ring` rejected the cryptographic signature.
43    #[error("Signature verification failed")]
44    SignatureVerificationFailed,
45    /// The attestation object could not be parsed.
46    ///
47    /// Note: this library parses the attestation object to extract
48    /// `authData` but does **not** verify attestation statement signatures.
49    #[error("Attestation error: {0}")]
50    AttestationError(String),
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_error_display_variants() {
59        let errors = vec![
60            WebauthnError::Config("test".to_string()),
61            WebauthnError::InvalidChallenge("test".to_string()),
62            WebauthnError::CredentialNotFound("test".to_string()),
63            WebauthnError::VerificationFailed("test".to_string()),
64            WebauthnError::DuplicateCredential("test".to_string()),
65            WebauthnError::UserNotFound("test".to_string()),
66            WebauthnError::ChallengeExpired,
67            WebauthnError::UnsupportedAlgorithm(-7),
68            WebauthnError::SignatureVerificationFailed,
69            WebauthnError::AttestationError("test".to_string()),
70        ];
71        for err in errors {
72            assert!(!format!("{err}").is_empty());
73            assert!(!format!("{err:?}").is_empty());
74        }
75    }
76}