1pub(crate) fn message(err: impl std::error::Error) -> String {
7 use std::fmt::Write;
8
9 let mut out = err.to_string();
10 let mut source = err.source();
11 while let Some(err) = source {
12 let _ = write!(out, ": {err}");
13 source = err.source();
14 }
15 out
16}
17
18#[derive(Debug, thiserror::Error)]
20#[non_exhaustive]
21pub enum KeyError {
22 #[error("invalid algorithm for key type")]
23 InvalidAlgorithm,
24
25 #[error("invalid algorithm for {0} curve")]
26 InvalidAlgorithmForCurve(&'static str),
27
28 #[error("invalid coordinate length for {0}")]
29 InvalidCoordinateLength(&'static str),
30
31 #[error("invalid curve for {0} key")]
32 InvalidCurve(&'static str),
33
34 #[error("missing private key")]
35 MissingPrivateKey,
36
37 #[error("oct key secret must be at least {0} bytes")]
38 SecretTooShort(usize),
39
40 #[error("OCT key cannot be converted to public key")]
41 NoPublicKey,
42
43 #[error("key does not support verification")]
44 VerifyUnsupported,
45
46 #[error("key does not support signing")]
47 SignUnsupported,
48
49 #[error("cannot find signing key")]
50 NoSigningKey,
51
52 #[error("cannot find key with kid {0}")]
53 KeyNotFound(String),
54
55 #[error("missing kid in JWT header")]
56 MissingKid,
57
58 #[error("missing x() point in EC key")]
59 MissingEcX,
60
61 #[error("missing y() point in EC key")]
62 MissingEcY,
63}
64
65#[derive(Debug, thiserror::Error)]
67#[non_exhaustive]
68pub enum Error {
69 #[error(transparent)]
70 Key(#[from] KeyError),
71
72 #[error("no publish or subscribe allowed; token is useless")]
73 UselessToken,
74
75 #[error("path `{0}` does not overlap the token root")]
76 RootMismatch(String),
77
78 #[error("token grants no access to path `{0}`")]
79 NoAccess(String),
80
81 #[error("no publish or subscribe allowed; key scope is useless")]
82 UselessScope,
83
84 #[error("token capabilities exceed the key scope")]
85 ScopeExceeded,
86
87 #[error("invalid algorithm: {0}")]
88 InvalidAlgorithm(String),
89
90 #[error("token has expired")]
91 TokenExpired,
92
93 #[error("token is not valid yet")]
94 TokenNotYetValid,
95
96 #[error(transparent)]
97 Pattern(#[from] moq_pattern::InvalidPattern),
98
99 #[error("grant names nothing; the session is refused")]
100 UselessGrant,
101
102 #[error("grant asks to be revalidated but never expires")]
103 UnboundedRevalidate,
104
105 #[error("session limits need a revalidate cadence, which ages out the slots of a relay that died")]
106 LimitsWithoutRevalidate,
107
108 #[error("the grant bound reaches past the system clock's range")]
109 ExpiresOutOfRange,
110
111 #[error("grant asks to be revalidated at no interval")]
112 ZeroRevalidate,
113
114 #[error("grant has already expired")]
115 GrantExpired,
116
117 #[error("the auth server refused the session")]
118 Refused,
119
120 #[error("auth server unavailable: {0}")]
121 Unavailable(String),
122
123 #[error("auth URL must be https://, unix://, or http:// to a loopback address: {0}")]
124 InsecureUrl(String),
125
126 #[error("invalid auth URL: {0}")]
127 InvalidUrl(String),
128
129 #[error("{0}")]
131 Json(String),
132
133 #[error(transparent)]
134 Io(#[from] std::io::Error),
135
136 #[error("{0}")]
138 Base64(String),
139
140 #[error(transparent)]
141 Utf8(#[from] std::string::FromUtf8Error),
142
143 #[error("{0}")]
145 Jwt(String),
146
147 #[error("{0}")]
149 Crypto(String),
150
151 #[error("{0}")]
153 Other(String),
154}
155
156macro_rules! from_message {
160 ($($ty:ty => $variant:ident),* $(,)?) => {
161 $(
162 impl From<$ty> for Error {
163 fn from(err: $ty) -> Self {
164 Self::$variant(message(err))
165 }
166 }
167 )*
168 };
169}
170
171from_message! {
172 serde_json::Error => Json,
173 base64::DecodeError => Base64,
174 jsonwebtoken::errors::Error => Jwt,
175 p256::elliptic_curve::pkcs8::Error => Crypto,
176 p256::elliptic_curve::Error => Crypto,
177 rsa::Error => Crypto,
178 rsa::pkcs1::Error => Crypto,
179 aws_lc_rs::error::Unspecified => Crypto,
180 aws_lc_rs::error::KeyRejected => Crypto,
181}
182
183#[cfg(feature = "client")]
184impl From<reqwest::Error> for Error {
185 fn from(err: reqwest::Error) -> Self {
186 Self::Unavailable(message(err.without_url()))
189 }
190}
191
192pub type Result<T> = std::result::Result<T, Error>;
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
201 fn message_flattens_the_source_chain() {
202 #[derive(Debug, thiserror::Error)]
203 #[error("inner")]
204 struct Inner;
205
206 #[derive(Debug, thiserror::Error)]
207 #[error("outer")]
208 struct Outer(#[source] Inner);
209
210 assert_eq!(message(Outer(Inner)), "outer: inner");
211 }
212}