Skip to main content

auths_verifier/
error.rs

1//! Error types for attestation and verification operations.
2
3use crate::core::Capability;
4use thiserror::Error;
5
6/// Trait for error metadata providing structured error codes and actionable suggestions.
7///
8/// All Auths error types implement this trait to provide:
9/// - A unique error code for programmatic handling (e.g., "AUTHS_VERIFICATION_ERROR")
10/// - An optional human-readable suggestion for how to resolve the error
11pub trait AuthsErrorInfo {
12    /// Returns a unique error code string following the AUTHS_* naming convention.
13    fn error_code(&self) -> &'static str;
14
15    /// Returns an optional actionable suggestion for resolving the error.
16    fn suggestion(&self) -> Option<&'static str>;
17}
18
19/// Errors returned by attestation signing, verification, and related operations.
20#[derive(Error, Debug)]
21pub enum AttestationError {
22    /// Cryptographic signature verification failed.
23    #[error("Signature verification failed: {0}")]
24    VerificationError(String),
25
26    /// The attestation does not grant the required capability.
27    #[error("Missing required capability: required {required:?}, available {available:?}")]
28    MissingCapability {
29        /// The capability that was required.
30        required: Capability,
31        /// The capabilities present in the attestation.
32        available: Vec<Capability>,
33    },
34
35    /// Signing the attestation data failed.
36    #[error("Signing failed: {0}")]
37    SigningError(String),
38
39    /// DID resolution failed.
40    #[error("DID resolution failed: {0}")]
41    DidResolutionError(String),
42
43    /// JSON serialization or deserialization failed.
44    #[error("Serialization error: {0}")]
45    SerializationError(String),
46
47    /// Caller provided invalid input data.
48    #[error("Invalid input: {0}")]
49    InvalidInput(String),
50
51    /// A cryptographic primitive (key parsing, hashing) failed.
52    #[error("Crypto error: {0}")]
53    CryptoError(String),
54
55    /// The JSON input exceeds the allowed size limit.
56    #[error("Input too large: {0}")]
57    InputTooLarge(String),
58
59    /// An unexpected internal error occurred.
60    #[error("Internal error: {0}")]
61    InternalError(String),
62
63    /// Organizational attestation signature verification failed.
64    #[error("Organizational Attestation verification failed: {0}")]
65    OrgVerificationFailed(String),
66
67    /// The organizational attestation has expired.
68    #[error("Organizational Attestation expired")]
69    OrgAttestationExpired,
70
71    /// DID resolution for the organization failed.
72    #[error("Organizational DID resolution failed: {0}")]
73    OrgDidResolutionFailed(String),
74
75    /// The identity bundle is older than its declared maximum age.
76    #[error("Bundle is {age_secs}s old (max {max_secs}s). Refresh with: auths id export-bundle")]
77    BundleExpired {
78        /// Actual bundle age in seconds.
79        age_secs: u64,
80        /// Maximum permitted age in seconds.
81        max_secs: u64,
82    },
83}
84
85impl AuthsErrorInfo for AttestationError {
86    fn error_code(&self) -> &'static str {
87        match self {
88            Self::VerificationError(_) => "AUTHS_VERIFICATION_ERROR",
89            Self::MissingCapability { .. } => "AUTHS_MISSING_CAPABILITY",
90            Self::SigningError(_) => "AUTHS_SIGNING_ERROR",
91            Self::DidResolutionError(_) => "AUTHS_DID_RESOLUTION_ERROR",
92            Self::SerializationError(_) => "AUTHS_SERIALIZATION_ERROR",
93            Self::InputTooLarge(_) => "AUTHS_INPUT_TOO_LARGE",
94            Self::InvalidInput(_) => "AUTHS_INVALID_INPUT",
95            Self::CryptoError(_) => "AUTHS_CRYPTO_ERROR",
96            Self::InternalError(_) => "AUTHS_INTERNAL_ERROR",
97            Self::OrgVerificationFailed(_) => "AUTHS_ORG_VERIFICATION_FAILED",
98            Self::OrgAttestationExpired => "AUTHS_ORG_ATTESTATION_EXPIRED",
99            Self::OrgDidResolutionFailed(_) => "AUTHS_ORG_DID_RESOLUTION_FAILED",
100            Self::BundleExpired { .. } => "AUTHS_BUNDLE_EXPIRED",
101        }
102    }
103
104    fn suggestion(&self) -> Option<&'static str> {
105        match self {
106            Self::VerificationError(_) => {
107                Some("Verify the attestation was signed with the correct key")
108            }
109            Self::MissingCapability { .. } => {
110                Some("Request an attestation with the required capability")
111            }
112            Self::DidResolutionError(_) => Some("Check that the DID is valid and resolvable"),
113            Self::OrgVerificationFailed(_) => {
114                Some("Verify organizational identity is properly configured")
115            }
116            Self::OrgAttestationExpired => {
117                Some("Request a new organizational attestation from the admin")
118            }
119            Self::OrgDidResolutionFailed(_) => {
120                Some("Check that the organization's DID is correctly configured")
121            }
122            // These typically don't have actionable suggestions
123            Self::InputTooLarge(_) => {
124                Some("Reduce the size of the JSON input or split into smaller batches")
125            }
126            Self::BundleExpired { .. } => Some(
127                "Re-export the bundle: auths id export-bundle --alias <ALIAS> --output bundle.json --max-age-secs <SECS>",
128            ),
129            Self::SigningError(_)
130            | Self::SerializationError(_)
131            | Self::InvalidInput(_)
132            | Self::CryptoError(_)
133            | Self::InternalError(_) => None,
134        }
135    }
136}