inth_oauth2_async/token/
refresh.rs

1use serde_json::Value;
2use std::time::{Duration, SystemTime};
3
4use crate::client::response::{FromResponse, ParseError};
5use crate::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: SystemTime,
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) -> SystemTime { self.expires }
22}
23
24impl Lifetime for Refresh {
25    fn expired(&self) -> bool { self.expires < SystemTime::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: SystemTime::now() + Duration::from_secs(expires_in.try_into().unwrap_or(0)),
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: SystemTime::now() + Duration::from_secs(expires_in.try_into().unwrap_or(0)),
60        })
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn from_response() {
70        let json = r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#.parse().unwrap();
71        let refresh = Refresh::from_response(&json).unwrap();
72        assert_eq!("aaaaaaaa", refresh.refresh_token);
73        assert!(refresh.expires > SystemTime::now());
74        assert!(refresh.expires <= SystemTime::now() + Duration::from_secs(3600));
75    }
76
77    #[test]
78    fn from_response_inherit() {
79        let json = r#"{"expires_in":3600}"#.parse().unwrap();
80        let prev = Refresh {
81            refresh_token: String::from("aaaaaaaa"),
82            expires: SystemTime::now(),
83        };
84        let refresh = Refresh::from_response_inherit(&json, &prev).unwrap();
85        assert_eq!("aaaaaaaa", refresh.refresh_token);
86        assert!(refresh.expires > SystemTime::now());
87        assert!(refresh.expires <= SystemTime::now() + Duration::from_secs(3600));
88    }
89}