inth_oauth2_async/
error.rs

1//! Errors.
2
3use std::error::Error;
4use std::fmt;
5
6use serde_json::Value;
7
8use crate::client::response::{FromResponse, ParseError};
9
10/// OAuth 2.0 error codes.
11///
12/// See [RFC 6749, section 5.2](http://tools.ietf.org/html/rfc6749#section-5.2).
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum OAuth2ErrorCode {
15    /// The request is missing a required parameter, includes an unsupported parameter value (other
16    /// than grant type), repeats a parameter, includes multiple credentials, utilizes more than
17    /// one mechanism for authenticating the client, or is otherwise malformed.
18    InvalidRequest,
19
20    /// Client authentication failed (e.g., unknown client, no client authentication included, or
21    /// unsupported authentication method).
22    InvalidClient,
23
24    /// The provided authorization grant (e.g., authorization code, resource owner credentials) or
25    /// refresh token is invalid, expired, revoked, does not match the redirection URI used in the
26    /// authorization request, or was issued to another client.
27    InvalidGrant,
28
29    /// The authenticated client is not authorized to use this authorization grant type.
30    UnauthorizedClient,
31
32    /// The authorization grant type is not supported by the authorization server.
33    UnsupportedGrantType,
34
35    /// The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the
36    /// resource owner.
37    InvalidScope,
38
39    /// An unrecognized error code, not defined in RFC 6749.
40    Unrecognized(String),
41}
42
43impl<'a> From<&'a str> for OAuth2ErrorCode {
44    fn from(s: &str) -> OAuth2ErrorCode {
45        match s {
46            "invalid_request" => OAuth2ErrorCode::InvalidRequest,
47            "invalid_client" => OAuth2ErrorCode::InvalidClient,
48            "invalid_grant" => OAuth2ErrorCode::InvalidGrant,
49            "unauthorized_client" => OAuth2ErrorCode::UnauthorizedClient,
50            "unsupported_grant_type" => OAuth2ErrorCode::UnsupportedGrantType,
51            "invalid_scope" => OAuth2ErrorCode::InvalidScope,
52            s => OAuth2ErrorCode::Unrecognized(s.to_owned()),
53        }
54    }
55}
56
57/// OAuth 2.0 error.
58///
59/// See [RFC 6749, section 5.2](http://tools.ietf.org/html/rfc6749#section-5.2).
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct OAuth2Error {
62    /// Error code.
63    pub code: OAuth2ErrorCode,
64
65    /// Human-readable text providing additional information about the error.
66    pub description: Option<String>,
67
68    /// A URI identifying a human-readable web page with information about the error.
69    pub uri: Option<String>,
70}
71
72impl fmt::Display for OAuth2Error {
73    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
74        write!(f, "{:?}", self.code)?;
75        if let Some(ref description) = self.description {
76            write!(f, ": {}", description)?;
77        }
78        if let Some(ref uri) = self.uri {
79            write!(f, " ({})", uri)?;
80        }
81        Ok(())
82    }
83}
84
85impl Error for OAuth2Error {
86    fn description(&self) -> &str { "OAuth 2.0 API error" }
87}
88
89impl FromResponse for OAuth2Error {
90    fn from_response(json: &Value) -> Result<Self, ParseError> {
91        let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
92
93        let code = obj.get("error")
94            .and_then(Value::as_str)
95            .ok_or(ParseError::ExpectedFieldType("error", "string"))?;
96        let description = obj.get("error_description").and_then(Value::as_str);
97        let uri = obj.get("error_uri").and_then(Value::as_str);
98
99        Ok(OAuth2Error {
100            code: code.into(),
101            description: description.map(Into::into),
102            uri: uri.map(Into::into),
103        })
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn from_response_empty() {
113        let json = "{}".parse().unwrap();
114        assert_eq!(
115            ParseError::ExpectedFieldType("error", "string"),
116            OAuth2Error::from_response(&json).unwrap_err()
117        );
118    }
119
120    #[test]
121    fn from_response() {
122        let json = r#"{"error":"invalid_request"}"#.parse().unwrap();
123        assert_eq!(
124            OAuth2Error {
125                code: OAuth2ErrorCode::InvalidRequest,
126                description: None,
127                uri: None,
128            },
129            OAuth2Error::from_response(&json).unwrap()
130        );
131    }
132
133    #[test]
134    fn from_response_with_description() {
135        let json = r#"{"error":"invalid_request","error_description":"foo"}"#
136            .parse()
137            .unwrap();
138        assert_eq!(
139            OAuth2Error {
140                code: OAuth2ErrorCode::InvalidRequest,
141                description: Some(String::from("foo")),
142                uri: None,
143            },
144            OAuth2Error::from_response(&json).unwrap()
145        );
146    }
147
148    #[test]
149    fn from_response_with_uri() {
150        let json = r#"{"error":"invalid_request","error_uri":"http://example.com"}"#
151            .parse()
152            .unwrap();
153        assert_eq!(
154            OAuth2Error {
155                code: OAuth2ErrorCode::InvalidRequest,
156                description: None,
157                uri: Some(String::from("http://example.com")),
158            },
159            OAuth2Error::from_response(&json).unwrap()
160        );
161    }
162}