inth_oauth2/token/
refresh.rs

1use chrono::{DateTime, Utc, Duration};
2use serde_json::Value;
3
4use client::response::{FromResponse, ParseError};
5use token::Lifetime;
6
7/// An expiring token which can be refreshed.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct Refresh {
10    refresh_token: String,
11    expires: DateTime<Utc>,
12}
13
14impl Refresh {
15    /// Returns the refresh token.
16    ///
17    /// See [RFC 6749, section 1.5](http://tools.ietf.org/html/rfc6749#section-1.5).
18    pub fn refresh_token(&self) -> &str { &self.refresh_token }
19
20    /// Returns the expiry time of the access token.
21    pub fn expires(&self) -> &DateTime<Utc> { &self.expires }
22}
23
24impl Lifetime for Refresh {
25    fn expired(&self) -> bool { self.expires < Utc::now() }
26}
27
28impl FromResponse for Refresh {
29    fn from_response(json: &Value) -> Result<Self, ParseError> {
30        let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
31
32        let refresh_token = obj.get("refresh_token")
33            .and_then(Value::as_str)
34            .ok_or(ParseError::ExpectedFieldType("refresh_token", "string"))?;
35        let expires_in = obj.get("expires_in")
36            .and_then(Value::as_i64)
37            .ok_or(ParseError::ExpectedFieldType("expires_in", "i64"))?;
38
39        Ok(Refresh {
40            refresh_token: refresh_token.into(),
41            expires: Utc::now() + Duration::seconds(expires_in),
42        })
43    }
44
45    fn from_response_inherit(json: &Value, prev: &Self) -> Result<Self, ParseError> {
46        let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
47
48        let refresh_token = obj.get("refresh_token")
49            .and_then(Value::as_str)
50            .or(Some(&prev.refresh_token))
51            .ok_or(ParseError::ExpectedFieldType("refresh_token", "string"))?;
52
53        let expires_in = obj.get("expires_in")
54            .and_then(Value::as_i64)
55            .ok_or(ParseError::ExpectedFieldType("expires_in", "i64"))?;
56
57        Ok(Refresh {
58            refresh_token: refresh_token.into(),
59            expires: Utc::now() + Duration::seconds(expires_in),
60        })
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use chrono::{Utc, Duration};
67
68    use client::response::FromResponse;
69    use super::Refresh;
70
71    #[test]
72    fn from_response() {
73        let json = r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#.parse().unwrap();
74        let refresh = Refresh::from_response(&json).unwrap();
75        assert_eq!("aaaaaaaa", refresh.refresh_token);
76        assert!(refresh.expires > Utc::now());
77        assert!(refresh.expires <= Utc::now() + Duration::seconds(3600));
78    }
79
80    #[test]
81    fn from_response_inherit() {
82        let json = r#"{"expires_in":3600}"#.parse().unwrap();
83        let prev = Refresh {
84            refresh_token: String::from("aaaaaaaa"),
85            expires: Utc::now(),
86        };
87        let refresh = Refresh::from_response_inherit(&json, &prev).unwrap();
88        assert_eq!("aaaaaaaa", refresh.refresh_token);
89        assert!(refresh.expires > Utc::now());
90        assert!(refresh.expires <= Utc::now() + Duration::seconds(3600));
91    }
92}