inth_oauth2/
error.rs

1//! Errors.
2
3use std::error::Error;
4use std::fmt;
5
6use serde_json::Value;
7
8use 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 client::response::{FromResponse, ParseError};
110    use super::{OAuth2Error, OAuth2ErrorCode};
111
112    #[test]
113    fn from_response_empty() {
114        let json = "{}".parse().unwrap();
115        assert_eq!(
116            ParseError::ExpectedFieldType("error", "string"),
117            OAuth2Error::from_response(&json).unwrap_err()
118        );
119    }
120
121    #[test]
122    fn from_response() {
123        let json = r#"{"error":"invalid_request"}"#.parse().unwrap();
124        assert_eq!(
125            OAuth2Error {
126                code: OAuth2ErrorCode::InvalidRequest,
127                description: None,
128                uri: None,
129            },
130            OAuth2Error::from_response(&json).unwrap()
131        );
132    }
133
134    #[test]
135    fn from_response_with_description() {
136        let json = r#"{"error":"invalid_request","error_description":"foo"}"#
137            .parse()
138            .unwrap();
139        assert_eq!(
140            OAuth2Error {
141                code: OAuth2ErrorCode::InvalidRequest,
142                description: Some(String::from("foo")),
143                uri: None,
144            },
145            OAuth2Error::from_response(&json).unwrap()
146        );
147    }
148
149    #[test]
150    fn from_response_with_uri() {
151        let json = r#"{"error":"invalid_request","error_uri":"http://example.com"}"#
152            .parse()
153            .unwrap();
154        assert_eq!(
155            OAuth2Error {
156                code: OAuth2ErrorCode::InvalidRequest,
157                description: None,
158                uri: Some(String::from("http://example.com")),
159            },
160            OAuth2Error::from_response(&json).unwrap()
161        );
162    }
163}