yup_oauth2/
error.rs

1//! Module containing various error types.
2
3use 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/// Error returned by the authorization server.
17///
18/// <https://tools.ietf.org/html/rfc6749#section-5.2>
19/// <https://tools.ietf.org/html/rfc8628#section-3.5>
20#[derive(Deserialize, Debug, PartialEq, Eq)]
21pub struct AuthError {
22    /// Error code from the server.
23    pub error: AuthErrorCode,
24    /// Human-readable text providing additional information.
25    pub error_description: Option<String>,
26    /// A URI identifying a human-readable web page with information about the error.
27    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/// The error code returned by the authorization server.
45#[derive(Debug, Clone, Eq, PartialEq)]
46pub enum AuthErrorCode {
47    /// invalid_request
48    InvalidRequest,
49    /// invalid_client
50    InvalidClient,
51    /// invalid_grant
52    InvalidGrant,
53    /// unauthorized_client
54    UnauthorizedClient,
55    /// unsupported_grant_type
56    UnsupportedGrantType,
57    /// invalid_scope
58    InvalidScope,
59    /// access_denied
60    AccessDenied,
61    /// expired_token
62    ExpiredToken,
63    /// other error
64    Other(String),
65}
66
67impl AuthErrorCode {
68    /// The error code as a &str
69    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/// A helper type to deserialize either an AuthError or another piece of data.
134#[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/// Encapsulates all possible results of the `token(...)` operation
151#[derive(Debug, ThisError)]
152pub enum Error {
153    /// Indicates connection failure
154    #[error("Connection failure: {0}")]
155    HttpError(#[from] HyperError),
156    /// Indicates connection failure
157    #[error("Connection failure: {0}")]
158    HttpClientError(#[from] SendError),
159    /// The server returned an error.
160    #[error("Server error: {0}")]
161    AuthError(#[from] AuthError),
162    /// Error while decoding a JSON response.
163    #[error("JSON Error; this might be a bug with unexpected server responses! {0}")]
164    JSONError(#[from] serde_json::Error),
165    /// Error within user input.
166    #[error("Invalid user input: {0}")]
167    UserError(String),
168    /// A lower level IO error.
169    #[error("Low level error: {0}")]
170    LowLevelError(#[from] io::Error),
171    /// We required an access token, but received a response that didn't contain one.
172    #[error("Expected an access token, but received a response without one")]
173    MissingAccessToken,
174    /// Produced by storage provider
175    #[error("Error while setting token in cache: {0}")]
176    StorageError(#[from] TokenStorageError),
177    /// Error while parsing credential source
178    #[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}