1use std::borrow::Cow;
4use std::error::Error as StdError;
5use std::fmt;
6use std::io;
7
8use hyper::Error as HyperError;
9use serde::Deserialize;
10use thiserror::Error as ThisError;
11
12pub use crate::client::SendError;
13pub use crate::external_account::CredentialSourceError;
14pub use crate::storage::TokenStorageError;
15
16#[derive(Deserialize, Debug, PartialEq, Eq)]
21pub struct AuthError {
22 pub error: AuthErrorCode,
24 pub error_description: Option<String>,
26 pub error_uri: Option<String>,
28}
29
30impl fmt::Display for AuthError {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 write!(f, "{}", &self.error.as_str())?;
33 if let Some(desc) = &self.error_description {
34 write!(f, ": {}", desc)?;
35 }
36 if let Some(uri) = &self.error_uri {
37 write!(f, "; See {} for more info", uri)?;
38 }
39 Ok(())
40 }
41}
42impl StdError for AuthError {}
43
44#[derive(Debug, Clone, Eq, PartialEq)]
46pub enum AuthErrorCode {
47 InvalidRequest,
49 InvalidClient,
51 InvalidGrant,
53 UnauthorizedClient,
55 UnsupportedGrantType,
57 InvalidScope,
59 AccessDenied,
61 ExpiredToken,
63 Other(String),
65}
66
67impl AuthErrorCode {
68 pub fn as_str(&self) -> &str {
70 match self {
71 AuthErrorCode::InvalidRequest => "invalid_request",
72 AuthErrorCode::InvalidClient => "invalid_client",
73 AuthErrorCode::InvalidGrant => "invalid_grant",
74 AuthErrorCode::UnauthorizedClient => "unauthorized_client",
75 AuthErrorCode::UnsupportedGrantType => "unsupported_grant_type",
76 AuthErrorCode::InvalidScope => "invalid_scope",
77 AuthErrorCode::AccessDenied => "access_denied",
78 AuthErrorCode::ExpiredToken => "expired_token",
79 AuthErrorCode::Other(s) => s.as_str(),
80 }
81 }
82
83 fn from_string<'a>(s: impl Into<Cow<'a, str>>) -> AuthErrorCode {
84 let s = s.into();
85 match s.as_ref() {
86 "invalid_request" => AuthErrorCode::InvalidRequest,
87 "invalid_client" => AuthErrorCode::InvalidClient,
88 "invalid_grant" => AuthErrorCode::InvalidGrant,
89 "unauthorized_client" => AuthErrorCode::UnauthorizedClient,
90 "unsupported_grant_type" => AuthErrorCode::UnsupportedGrantType,
91 "invalid_scope" => AuthErrorCode::InvalidScope,
92 "access_denied" => AuthErrorCode::AccessDenied,
93 "expired_token" => AuthErrorCode::ExpiredToken,
94 _ => AuthErrorCode::Other(s.into_owned()),
95 }
96 }
97}
98
99impl From<String> for AuthErrorCode {
100 fn from(s: String) -> Self {
101 AuthErrorCode::from_string(s)
102 }
103}
104
105impl From<&str> for AuthErrorCode {
106 fn from(s: &str) -> Self {
107 AuthErrorCode::from_string(s)
108 }
109}
110
111impl<'de> Deserialize<'de> for AuthErrorCode {
112 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113 where
114 D: serde::Deserializer<'de>,
115 {
116 struct V;
117 impl serde::de::Visitor<'_> for V {
118 type Value = AuthErrorCode;
119 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120 f.write_str("any string")
121 }
122 fn visit_string<E: serde::de::Error>(self, value: String) -> Result<Self::Value, E> {
123 Ok(value.into())
124 }
125 fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
126 Ok(value.into())
127 }
128 }
129 deserializer.deserialize_string(V)
130 }
131}
132
133#[derive(Deserialize, Debug)]
135#[serde(untagged)]
136pub(crate) enum AuthErrorOr<T> {
137 AuthError(AuthError),
138 Data(T),
139}
140
141impl<T> AuthErrorOr<T> {
142 pub(crate) fn into_result(self) -> Result<T, AuthError> {
143 match self {
144 AuthErrorOr::AuthError(err) => Result::Err(err),
145 AuthErrorOr::Data(value) => Result::Ok(value),
146 }
147 }
148}
149
150#[derive(Debug, ThisError)]
152pub enum Error {
153 #[error("Connection failure: {0}")]
155 HttpError(#[from] HyperError),
156 #[error("Connection failure: {0}")]
158 HttpClientError(#[from] SendError),
159 #[error("Server error: {0}")]
161 AuthError(#[from] AuthError),
162 #[error("JSON Error; this might be a bug with unexpected server responses! {0}")]
164 JSONError(#[from] serde_json::Error),
165 #[error("Invalid user input: {0}")]
167 UserError(String),
168 #[error("Low level error: {0}")]
170 LowLevelError(#[from] io::Error),
171 #[error("Expected an access token, but received a response without one")]
173 MissingAccessToken,
174 #[error("Error while setting token in cache: {0}")]
176 StorageError(#[from] TokenStorageError),
177 #[error("Credential source is invalid: {0}")]
179 CredentialSourceError(CredentialSourceError),
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_auth_error_code_deserialize() {
188 assert_eq!(
189 AuthErrorCode::InvalidRequest,
190 serde_json::from_str(r#""invalid_request""#).unwrap()
191 );
192 assert_eq!(
193 AuthErrorCode::InvalidClient,
194 serde_json::from_str(r#""invalid_client""#).unwrap()
195 );
196 assert_eq!(
197 AuthErrorCode::InvalidGrant,
198 serde_json::from_str(r#""invalid_grant""#).unwrap()
199 );
200 assert_eq!(
201 AuthErrorCode::UnauthorizedClient,
202 serde_json::from_str(r#""unauthorized_client""#).unwrap()
203 );
204 assert_eq!(
205 AuthErrorCode::UnsupportedGrantType,
206 serde_json::from_str(r#""unsupported_grant_type""#).unwrap()
207 );
208 assert_eq!(
209 AuthErrorCode::InvalidScope,
210 serde_json::from_str(r#""invalid_scope""#).unwrap()
211 );
212 assert_eq!(
213 AuthErrorCode::AccessDenied,
214 serde_json::from_str(r#""access_denied""#).unwrap()
215 );
216 assert_eq!(
217 AuthErrorCode::ExpiredToken,
218 serde_json::from_str(r#""expired_token""#).unwrap()
219 );
220 assert_eq!(
221 AuthErrorCode::Other("undefined".to_owned()),
222 serde_json::from_str(r#""undefined""#).unwrap()
223 );
224 }
225}