Skip to main content

aauth_protocol/
error.rs

1//! Errors. A signature or token that does not verify carries the registered
2//! code a peer is answered with; everything else is an [`Error`] variant
3//! naming what kind of thing went wrong.
4
5/// An HTTP signature that does not verify, answered with `401` and
6/// `Signature-Error: error=<code>` ([`SignatureError::header_value`]).
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("{code}: {detail}")]
9pub struct SignatureError {
10    /// The registered code: `invalid_signature`, `invalid_input`,
11    /// `invalid_key`, `unknown_key`, `invalid_jwt`, `unsupported_scheme`,
12    /// `unsupported_algorithm` or `clock_skew`.
13    pub code: &'static str,
14    /// A human-readable explanation, for logs. Not part of the protocol.
15    pub detail: String,
16}
17
18impl SignatureError {
19    /// A signature error with `code` and `detail`.
20    pub fn new(code: &'static str, detail: impl Into<String>) -> Self {
21        Self {
22            code,
23            detail: detail.into(),
24        }
25    }
26
27    /// The `Signature-Error` response header value.
28    pub fn header_value(&self) -> String {
29        format!("error={}", self.code)
30    }
31}
32
33/// A token that does not verify.
34#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
35#[error("{code}: {detail}")]
36pub struct TokenError {
37    /// The code: `invalid_agent_token`, `invalid_person_token`,
38    /// `invalid_auth_token`, `malformed_token`, `unsupported_token_type`,
39    /// `token_type_not_accepted`, `token_expired`, `key_binding_failed`,
40    /// `aud_mismatch`, `clock_skew` or `invalid_configuration`.
41    ///
42    /// `token_expired` and `clock_skew` are reported only once the issuer's
43    /// signature has verified, so a presenter can act on them: refresh the
44    /// token, or wait out the skew.
45    pub code: &'static str,
46    /// A human-readable explanation, for logs. Not part of the protocol.
47    pub detail: String,
48}
49
50impl TokenError {
51    /// A token error with `code` and `detail`.
52    pub fn new(code: &'static str, detail: impl Into<String>) -> Self {
53        Self {
54            code,
55            detail: detail.into(),
56        }
57    }
58}
59
60/// Everything else that can go wrong.
61#[derive(Debug, thiserror::Error)]
62#[non_exhaustive]
63pub enum Error {
64    /// An HTTP signature did not verify.
65    #[error(transparent)]
66    Signature(#[from] SignatureError),
67    /// A token did not verify.
68    #[error(transparent)]
69    Token(#[from] TokenError),
70    /// A key that cannot be read or used.
71    #[error("invalid key: {0}")]
72    Key(String),
73    /// A URL that cannot be parsed, or that this client refuses to call.
74    #[error("url: {0}")]
75    Url(String),
76    /// A metadata document or key set that cannot be fetched, read or trusted.
77    #[error("discovery: {0}")]
78    Discovery(String),
79    /// A peer that could not be reached, failed, or answered with something
80    /// unusable. Never a decision: a refusal is an answer, not an error.
81    #[error("http: {0}")]
82    Http(String),
83    /// A value that is not what the protocol requires.
84    #[error("invalid: {0}")]
85    Invalid(String),
86}
87
88/// `Result` with this crate's [`Error`].
89pub type Result<T, E = Error> = std::result::Result<T, E>;