dbrest_core/auth/
error.rs1use std::fmt;
8
9#[derive(Debug, Clone)]
20pub enum JwtError {
21 SecretMissing,
23
24 Decode(JwtDecodeError),
26
27 TokenRequired,
29
30 Claims(JwtClaimsError),
32}
33
34#[derive(Debug, Clone)]
36pub enum JwtDecodeError {
37 EmptyAuthHeader,
39 UnexpectedParts(usize),
41 KeyError(String),
43 BadAlgorithm(String),
45 BadCrypto,
47 UnsupportedTokenType,
49}
50
51#[derive(Debug, Clone)]
53pub enum JwtClaimsError {
54 Expired,
56 NotYetValid,
58 IssuedAtFuture,
60 NotInAudience,
62 ParsingFailed,
64}
65
66impl fmt::Display for JwtError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 JwtError::SecretMissing => write!(f, "Server lacks JWT secret"),
74 JwtError::Decode(e) => write!(f, "{e}"),
75 JwtError::TokenRequired => write!(f, "Anonymous access is disabled"),
76 JwtError::Claims(e) => write!(f, "{e}"),
77 }
78 }
79}
80
81impl fmt::Display for JwtDecodeError {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self {
84 JwtDecodeError::EmptyAuthHeader => {
85 write!(f, "Empty JWT is sent in Authorization header")
86 }
87 JwtDecodeError::UnexpectedParts(n) => {
88 write!(f, "Expected 3 parts in JWT; got {n}")
89 }
90 JwtDecodeError::KeyError(_) => {
91 write!(f, "No suitable key or wrong key type")
92 }
93 JwtDecodeError::BadAlgorithm(_) => {
94 write!(f, "Wrong or unsupported encoding algorithm")
95 }
96 JwtDecodeError::BadCrypto => {
97 write!(f, "JWT cryptographic operation failed")
98 }
99 JwtDecodeError::UnsupportedTokenType => {
100 write!(f, "Unsupported token type")
101 }
102 }
103 }
104}
105
106impl fmt::Display for JwtClaimsError {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 match self {
109 JwtClaimsError::Expired => write!(f, "JWT expired"),
110 JwtClaimsError::NotYetValid => write!(f, "JWT not yet valid"),
111 JwtClaimsError::IssuedAtFuture => write!(f, "JWT issued at future"),
112 JwtClaimsError::NotInAudience => write!(f, "JWT not in audience"),
113 JwtClaimsError::ParsingFailed => write!(f, "Parsing claims failed"),
114 }
115 }
116}
117
118impl std::error::Error for JwtError {}
119impl std::error::Error for JwtDecodeError {}
120impl std::error::Error for JwtClaimsError {}
121
122impl From<JwtDecodeError> for JwtError {
127 fn from(e: JwtDecodeError) -> Self {
128 JwtError::Decode(e)
129 }
130}
131
132impl From<JwtClaimsError> for JwtError {
133 fn from(e: JwtClaimsError) -> Self {
134 JwtError::Claims(e)
135 }
136}
137
138impl JwtError {
143 pub fn code(&self) -> &'static str {
145 match self {
146 JwtError::SecretMissing => "DBRST300",
147 JwtError::Decode(_) => "DBRST301",
148 JwtError::TokenRequired => "DBRST302",
149 JwtError::Claims(_) => "DBRST303",
150 }
151 }
152
153 pub fn status(&self) -> http::StatusCode {
155 match self {
156 JwtError::SecretMissing => http::StatusCode::INTERNAL_SERVER_ERROR,
157 JwtError::Decode(_) => http::StatusCode::UNAUTHORIZED,
158 JwtError::TokenRequired => http::StatusCode::UNAUTHORIZED,
159 JwtError::Claims(_) => http::StatusCode::UNAUTHORIZED,
160 }
161 }
162
163 pub fn details(&self) -> Option<String> {
165 match self {
166 JwtError::Decode(JwtDecodeError::KeyError(d)) => Some(d.clone()),
167 JwtError::Decode(JwtDecodeError::BadAlgorithm(d)) => Some(d.clone()),
168 _ => None,
169 }
170 }
171
172 pub fn www_authenticate(&self) -> Option<String> {
174 match self {
175 JwtError::TokenRequired => Some("Bearer".to_string()),
176 JwtError::Decode(e) => {
177 let msg = e.to_string();
178 Some(format!(
179 "Bearer error=\"invalid_token\", error_description=\"{msg}\""
180 ))
181 }
182 JwtError::Claims(e) => {
183 let msg = e.to_string();
184 Some(format!(
185 "Bearer error=\"invalid_token\", error_description=\"{msg}\""
186 ))
187 }
188 JwtError::SecretMissing => None,
189 }
190 }
191}
192
193#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_error_codes() {
203 assert_eq!(JwtError::SecretMissing.code(), "DBRST300");
204 assert_eq!(
205 JwtError::Decode(JwtDecodeError::EmptyAuthHeader).code(),
206 "DBRST301"
207 );
208 assert_eq!(JwtError::TokenRequired.code(), "DBRST302");
209 assert_eq!(JwtError::Claims(JwtClaimsError::Expired).code(), "DBRST303");
210 }
211
212 #[test]
213 fn test_error_status() {
214 assert_eq!(
215 JwtError::SecretMissing.status(),
216 http::StatusCode::INTERNAL_SERVER_ERROR
217 );
218 assert_eq!(
219 JwtError::Decode(JwtDecodeError::BadCrypto).status(),
220 http::StatusCode::UNAUTHORIZED
221 );
222 assert_eq!(
223 JwtError::TokenRequired.status(),
224 http::StatusCode::UNAUTHORIZED
225 );
226 assert_eq!(
227 JwtError::Claims(JwtClaimsError::NotInAudience).status(),
228 http::StatusCode::UNAUTHORIZED
229 );
230 }
231
232 #[test]
233 fn test_www_authenticate_headers() {
234 let hdr = JwtError::TokenRequired.www_authenticate().unwrap();
236 assert_eq!(hdr, "Bearer");
237
238 let hdr = JwtError::Decode(JwtDecodeError::BadCrypto)
240 .www_authenticate()
241 .unwrap();
242 assert!(hdr.contains("invalid_token"));
243 assert!(hdr.contains("cryptographic"));
244
245 let hdr = JwtError::Claims(JwtClaimsError::Expired)
247 .www_authenticate()
248 .unwrap();
249 assert!(hdr.contains("expired"));
250
251 assert!(JwtError::SecretMissing.www_authenticate().is_none());
253 }
254
255 #[test]
256 fn test_display_messages() {
257 assert_eq!(
258 JwtError::SecretMissing.to_string(),
259 "Server lacks JWT secret"
260 );
261 assert_eq!(
262 JwtError::TokenRequired.to_string(),
263 "Anonymous access is disabled"
264 );
265 assert_eq!(
266 JwtDecodeError::UnexpectedParts(2).to_string(),
267 "Expected 3 parts in JWT; got 2"
268 );
269 assert_eq!(JwtClaimsError::Expired.to_string(), "JWT expired");
270 }
271
272 #[test]
273 fn test_details() {
274 let err = JwtError::Decode(JwtDecodeError::KeyError(
275 "None of the keys was able to decode the JWT".to_string(),
276 ));
277 assert!(err.details().unwrap().contains("keys"));
278
279 assert!(
280 JwtError::Claims(JwtClaimsError::Expired)
281 .details()
282 .is_none()
283 );
284 }
285}